@azure/communication-react 1.16.0-alpha-202404160012 → 1.16.0-alpha-202404180013

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 (42) hide show
  1. package/dist/communication-react.d.ts +1 -1
  2. package/dist/dist-cjs/communication-react/{ChatMessageComponentAsRichTextEditBox-CmrzlNzS.js → ChatMessageComponentAsRichTextEditBox-C3t87AZz.js} +3 -3
  3. package/dist/dist-cjs/communication-react/{ChatMessageComponentAsRichTextEditBox-CmrzlNzS.js.map → ChatMessageComponentAsRichTextEditBox-C3t87AZz.js.map} +1 -1
  4. package/dist/dist-cjs/communication-react/{index-CC43HCUd.js → index-BP7335hI.js} +207 -71
  5. package/dist/dist-cjs/communication-react/index-BP7335hI.js.map +1 -0
  6. package/dist/dist-cjs/communication-react/index.js +2 -2
  7. package/dist/dist-esm/acs-ui-common/src/telemetryVersion.js +1 -1
  8. package/dist/dist-esm/acs-ui-common/src/telemetryVersion.js.map +1 -1
  9. package/dist/dist-esm/calling-stateful-client/src/DeviceManagerDeclarative.js +68 -4
  10. package/dist/dist-esm/calling-stateful-client/src/DeviceManagerDeclarative.js.map +1 -1
  11. package/dist/dist-esm/react-components/src/components/RichTextEditor/Buttons/RichTextRibbonButtons.js +8 -4
  12. package/dist/dist-esm/react-components/src/components/RichTextEditor/Buttons/RichTextRibbonButtons.js.map +1 -1
  13. package/dist/dist-esm/react-components/src/components/RichTextEditor/Plugins/CopyPastePlugin.d.ts +9 -2
  14. package/dist/dist-esm/react-components/src/components/RichTextEditor/Plugins/CopyPastePlugin.js +44 -2
  15. package/dist/dist-esm/react-components/src/components/RichTextEditor/Plugins/CopyPastePlugin.js.map +1 -1
  16. package/dist/dist-esm/react-components/src/components/RichTextEditor/RichTextEditor.d.ts +14 -0
  17. package/dist/dist-esm/react-components/src/components/RichTextEditor/RichTextEditor.js +5 -0
  18. package/dist/dist-esm/react-components/src/components/RichTextEditor/RichTextEditor.js.map +1 -1
  19. package/dist/dist-esm/react-components/src/components/RichTextEditor/RichTextSendBox.js +49 -13
  20. package/dist/dist-esm/react-components/src/components/RichTextEditor/RichTextSendBox.js.map +1 -1
  21. package/dist/dist-esm/react-components/src/components/VideoGallery/DefaultLayout.js +3 -2
  22. package/dist/dist-esm/react-components/src/components/VideoGallery/DefaultLayout.js.map +1 -1
  23. package/dist/dist-esm/react-components/src/components/VideoGallery/FloatingLocalVideoLayout.js +3 -2
  24. package/dist/dist-esm/react-components/src/components/VideoGallery/FloatingLocalVideoLayout.js.map +1 -1
  25. package/dist/dist-esm/react-components/src/components/VideoGallery/LargeGalleryLayout.js +1 -2
  26. package/dist/dist-esm/react-components/src/components/VideoGallery/LargeGalleryLayout.js.map +1 -1
  27. package/dist/dist-esm/react-components/src/components/VideoGallery/SpeakerVideoLayout.js +1 -1
  28. package/dist/dist-esm/react-components/src/components/VideoGallery/SpeakerVideoLayout.js.map +1 -1
  29. package/dist/dist-esm/react-components/src/components/VideoGallery/utils/videoGalleryLayoutUtils.d.ts +5 -1
  30. package/dist/dist-esm/react-components/src/components/VideoGallery/utils/videoGalleryLayoutUtils.js +17 -17
  31. package/dist/dist-esm/react-components/src/components/VideoGallery/utils/videoGalleryLayoutUtils.js.map +1 -1
  32. package/dist/dist-esm/react-components/src/components/styles/RichTextEditor.styles.d.ts +1 -1
  33. package/dist/dist-esm/react-components/src/components/styles/RichTextEditor.styles.js +10 -21
  34. package/dist/dist-esm/react-components/src/components/styles/RichTextEditor.styles.js.map +1 -1
  35. package/dist/dist-esm/react-composites/src/composites/CallComposite/adapter/CallAdapter.d.ts +1 -1
  36. package/dist/dist-esm/react-composites/src/composites/CallComposite/adapter/CallAdapter.js.map +1 -1
  37. package/dist/dist-esm/react-composites/src/composites/CallComposite/components/LocalDeviceSettings.js +2 -6
  38. package/dist/dist-esm/react-composites/src/composites/CallComposite/components/LocalDeviceSettings.js.map +1 -1
  39. package/dist/dist-esm/react-composites/src/composites/common/SidePaneHeader.js +1 -1
  40. package/dist/dist-esm/react-composites/src/composites/common/SidePaneHeader.js.map +1 -1
  41. package/package.json +1 -1
  42. package/dist/dist-cjs/communication-react/index-CC43HCUd.js.map +0 -1
@@ -17,9 +17,9 @@ var reactFileTypeIcons = require('@fluentui/react-file-type-icons');
17
17
  var react$1 = require('@griffel/react');
18
18
  var roosterjsEditorPlugins = require('roosterjs-editor-plugins');
19
19
  var roosterjsEditorCore = require('roosterjs-editor-core');
20
+ var roosterjsEditorTypesCompatible = require('roosterjs-editor-types-compatible');
20
21
  var roosterjsReact = require('roosterjs-react');
21
22
  var roosterjsEditorDom = require('roosterjs-editor-dom');
22
- var roosterjsEditorTypesCompatible = require('roosterjs-editor-types-compatible');
23
23
  var roosterjsEditorApi = require('roosterjs-editor-api');
24
24
  var reactChat = require('@fluentui-contrib/react-chat');
25
25
  var uuid = require('uuid');
@@ -176,7 +176,7 @@ function getDefaultExportFromCjs (x) {
176
176
  // Copyright (c) Microsoft Corporation.
177
177
  // Licensed under the MIT License.
178
178
  // GENERATED FILE. DO NOT EDIT MANUALLY.
179
- var telemetryVersion = '1.16.0-alpha-202404160012';
179
+ var telemetryVersion = '1.16.0-alpha-202404180013';
180
180
 
181
181
 
182
182
  var telemetryVersion$1 = /*@__PURE__*/getDefaultExportFromCjs(telemetryVersion);
@@ -1451,6 +1451,24 @@ class ProxyDeviceManager {
1451
1451
  this._deviceManager.on('audioDevicesUpdated', this.audioDevicesUpdated);
1452
1452
  this._deviceManager.on('selectedMicrophoneChanged', this.selectedMicrophoneChanged);
1453
1453
  this._deviceManager.on('selectedSpeakerChanged', this.selectedSpeakerChanged);
1454
+ // Subscribe to browser camera permissions changes
1455
+ try {
1456
+ navigator.permissions.query({ name: 'camera' }).then((cameraPermissions) => {
1457
+ cameraPermissions.addEventListener('change', this.permissionsApiStateChangeHandler);
1458
+ });
1459
+ }
1460
+ catch (e) {
1461
+ console.info('Could not subscribe to Permissions API Camera changed events, API is not supported by browser', e);
1462
+ }
1463
+ // Subscribe to browser microphone permissions changes
1464
+ try {
1465
+ navigator.permissions.query({ name: 'microphone' }).then((micPermissions) => {
1466
+ micPermissions.addEventListener('change', this.permissionsApiStateChangeHandler);
1467
+ });
1468
+ }
1469
+ catch (e) {
1470
+ console.info('Could not subscribe to Permissions API Microphone changed events, API is not supported by browser', e);
1471
+ }
1454
1472
  };
1455
1473
  /**
1456
1474
  * This is used to unsubscribe DeclarativeDeviceManager from the DeviceManager events.
@@ -1460,7 +1478,28 @@ class ProxyDeviceManager {
1460
1478
  this._deviceManager.off('audioDevicesUpdated', this.audioDevicesUpdated);
1461
1479
  this._deviceManager.off('selectedMicrophoneChanged', this.selectedMicrophoneChanged);
1462
1480
  this._deviceManager.off('selectedSpeakerChanged', this.selectedSpeakerChanged);
1481
+ // Unsubscribe from browser camera permissions changes
1482
+ try {
1483
+ navigator.permissions.query({ name: 'camera' }).then((cameraPermissions) => {
1484
+ cameraPermissions.removeEventListener('change', this.permissionsApiStateChangeHandler);
1485
+ });
1486
+ }
1487
+ catch (e) {
1488
+ console.info('Could not Unsubscribe to Permissions API Camera changed events, API is not supported by browser', e);
1489
+ }
1490
+ // Unsubscribe from browser microphone permissions changes
1491
+ try {
1492
+ navigator.permissions.query({ name: 'microphone' }).then((micPermissions) => {
1493
+ micPermissions.removeEventListener('change', this.permissionsApiStateChangeHandler);
1494
+ });
1495
+ }
1496
+ catch (e) {
1497
+ console.info('Could not Unsubscribe to Permissions API Camera changed events, API is not supported by browser', e);
1498
+ }
1463
1499
  };
1500
+ this.permissionsApiStateChangeHandler = () => __awaiter$O(this, void 0, void 0, function* () {
1501
+ yield this.updateDevicePermissionState();
1502
+ });
1464
1503
  /**
1465
1504
  * Used to set a camera inside the proxy device manager.
1466
1505
  *
@@ -1485,6 +1524,32 @@ class ProxyDeviceManager {
1485
1524
  this.selectedSpeakerChanged = () => {
1486
1525
  this._context.setDeviceManagerSelectedSpeaker(this._deviceManager.selectedSpeaker);
1487
1526
  };
1527
+ this.updateDevicePermissionState = (sdkDeviceAccessState) => __awaiter$O(this, void 0, void 0, function* () {
1528
+ let hasCameraPermission = !!(sdkDeviceAccessState === null || sdkDeviceAccessState === void 0 ? void 0 : sdkDeviceAccessState.video);
1529
+ let hasMicPermission = !!(sdkDeviceAccessState === null || sdkDeviceAccessState === void 0 ? void 0 : sdkDeviceAccessState.audio);
1530
+ // Supplement the SDK values with values from the Permissions API to get a better understanding of the device
1531
+ // permission state. The SDK only uses the getUserMedia API to determine the device permission state. However,
1532
+ // this returns false if the camera is in use by another application. The Permissions API can provide more
1533
+ // information about the device permission state, but is not supported yet in Firefox or Android WebView.
1534
+ // Note: It also has the limitation where it cannot detect if the device is blocked by the Operating System
1535
+ // permissions.
1536
+ try {
1537
+ const [cameraPermissions, micPermissions] = yield Promise.all([
1538
+ navigator.permissions.query({ name: 'camera' }),
1539
+ navigator.permissions.query({ name: 'microphone' })
1540
+ ]);
1541
+ hasCameraPermission = cameraPermissions.state === 'granted';
1542
+ hasMicPermission = micPermissions.state === 'granted';
1543
+ }
1544
+ catch (e) {
1545
+ console.info('Permissions API is not supported by browser', e);
1546
+ }
1547
+ this._context.setDeviceManagerDeviceAccess({
1548
+ video: hasCameraPermission,
1549
+ audio: hasMicPermission
1550
+ });
1551
+ this.setDeviceManager();
1552
+ });
1488
1553
  this._deviceManager = deviceManager;
1489
1554
  this._context = context;
1490
1555
  this.setDeviceManager();
@@ -1535,11 +1600,10 @@ class ProxyDeviceManager {
1535
1600
  }
1536
1601
  case 'askDevicePermission': {
1537
1602
  return this._context.withAsyncErrorTeedToState((...args) => {
1538
- return target.askDevicePermission(...args).then((deviceAccess) => {
1539
- this._context.setDeviceManagerDeviceAccess(deviceAccess);
1540
- this.setDeviceManager();
1603
+ return target.askDevicePermission(...args).then((deviceAccess) => __awaiter$O(this, void 0, void 0, function* () {
1604
+ yield this.updateDevicePermissionState(deviceAccess);
1541
1605
  return deviceAccess;
1542
- });
1606
+ }));
1543
1607
  }, 'DeviceManager.askDevicePermission');
1544
1608
  }
1545
1609
  default:
@@ -9915,14 +9979,14 @@ const ribbonTableButtonRootStyles = (theme, isSelected) => {
9915
9979
  '.ribbon-table-button-regular-icon': {
9916
9980
  width: '1.25rem',
9917
9981
  height: '1.25rem',
9918
- marginTop: '-0.25rem',
9982
+ margin: '-0.25rem 0.25rem 0 0.25rem',
9919
9983
  color: theme.palette.neutralPrimary,
9920
9984
  display: isSelected ? 'none' : 'inline-block'
9921
9985
  },
9922
9986
  '.ribbon-table-button-filled-icon': {
9923
9987
  width: '1.25rem',
9924
9988
  height: '1.25rem',
9925
- marginTop: '-0.25rem',
9989
+ margin: '-0.25rem 0.25rem 0 0.25rem',
9926
9990
  color: theme.palette.themePrimary,
9927
9991
  display: isSelected ? 'inline-block' : 'none'
9928
9992
  }
@@ -9933,22 +9997,11 @@ const ribbonTableButtonRootStyles = (theme, isSelected) => {
9933
9997
  * @private
9934
9998
  */
9935
9999
  const ribbonDividerStyle = (theme) => {
9936
- return {
9937
- icon: { color: theme.palette.neutralQuaternaryAlt, margin: '0 -0.5rem', height: 'auto' },
9938
- root: { margin: '0', padding: '0', minWidth: 'auto' },
9939
- rootHovered: {
9940
- backgroundColor: 'transparent',
9941
- selectors: {
9942
- // Icon's color doesn't work here because of the specificity
9943
- '.ms-Button-icon': {
9944
- color: theme.palette.neutralQuaternaryAlt
9945
- },
9946
- '.ms-Button-menuIcon': {
9947
- color: theme.palette.neutralQuaternaryAlt
9948
- }
9949
- }
9950
- }
9951
- };
10000
+ return react.mergeStyles({
10001
+ color: theme.palette.neutralQuaternaryAlt,
10002
+ margin: '0 -0.5rem',
10003
+ paddingTop: '0.5rem'
10004
+ });
9952
10005
  };
9953
10006
  /**
9954
10007
  * @private
@@ -9971,7 +10024,7 @@ const richTextFormatButtonIconStyle = (theme, isSelected) => {
9971
10024
  const editBoxRichTextEditorStyle = () => {
9972
10025
  return {
9973
10026
  minHeight: '2.25rem',
9974
- maxHeight: '2.25rem'
10027
+ maxHeight: '8rem'
9975
10028
  };
9976
10029
  };
9977
10030
  /**
@@ -9979,8 +10032,8 @@ const editBoxRichTextEditorStyle = () => {
9979
10032
  */
9980
10033
  const sendBoxRichTextEditorStyle = (isExpanded) => {
9981
10034
  return {
9982
- minHeight: isExpanded ? '5rem' : '1.25rem',
9983
- maxHeight: '5rem'
10035
+ minHeight: isExpanded ? '4rem' : '1.25rem',
10036
+ maxHeight: '8rem'
9984
10037
  };
9985
10038
  };
9986
10039
  /**
@@ -10227,16 +10280,19 @@ const MaxColumnsNumber = 5;
10227
10280
  const dividerRibbonButton = (theme, key) => {
10228
10281
  return {
10229
10282
  key: key,
10230
- iconName: 'RichTextDividerIcon',
10283
+ // the icon will be set in `onRender` callback
10284
+ // this is needed to make the divider unavailable for keyboard navigation
10285
+ iconName: '',
10286
+ // no text is needed here as we don't want to show a tooltip for the divider
10231
10287
  unlocalizedText: '',
10232
10288
  onClick: () => { },
10233
10289
  isDisabled: () => true,
10234
10290
  commandBarProperties: {
10235
10291
  // show the item correctly for the overflow menu
10236
10292
  itemType: react.ContextualMenuItemType.Divider,
10237
- buttonStyles: ribbonDividerStyle(theme),
10238
10293
  // this is still needed to remove checkmark icon space even though it is a divider
10239
- canCheck: false
10294
+ canCheck: false,
10295
+ onRender: () => React.createElement(react.Icon, { iconName: "RichTextDividerIcon", className: ribbonDividerStyle(theme) })
10240
10296
  }
10241
10297
  };
10242
10298
  };
@@ -10404,13 +10460,22 @@ const getEditingTable = (editor, node) => {
10404
10460
  * CopyPastePlugin is a plugin for handling copy and paste events in the editor.
10405
10461
  */
10406
10462
  class CopyPastePlugin {
10463
+ constructor() {
10464
+ this.editor = null;
10465
+ }
10407
10466
  getName() {
10408
10467
  return 'CopyPastePlugin';
10409
10468
  }
10410
- initialize() { }
10469
+ initialize(editor) {
10470
+ this.editor = editor;
10471
+ }
10411
10472
  dispose() { }
10412
10473
  onPluginEvent(event) {
10413
10474
  removeImageElement(event);
10475
+ if (this.editor !== null && !this.editor.isDisposed()) {
10476
+ // scroll the editor to the correct position after pasting content
10477
+ scrollToBottomAfterContentPaste(event, this.editor);
10478
+ }
10414
10479
  }
10415
10480
  }
10416
10481
  /**
@@ -10432,6 +10497,38 @@ const removeImageElement = (event) => {
10432
10497
  });
10433
10498
  }
10434
10499
  };
10500
+ /**
10501
+ * Scrolls the editor's scroll container to the bottom after content is pasted.
10502
+ * @param event - The plugin event.
10503
+ * @param editor - The editor instance.
10504
+ */
10505
+ const scrollToBottomAfterContentPaste = (event, editor) => {
10506
+ if (event.eventType === roosterjsEditorTypesCompatible.CompatiblePluginEventType.ContentChanged && event.source === roosterjsEditorTypesCompatible.CompatibleChangeSource.Paste) {
10507
+ // current focused position in the editor
10508
+ const focusedPosition = editor.getFocusedPosition();
10509
+ // the cursor position relative to the viewport
10510
+ const cursorRect = focusedPosition && roosterjsEditorDom.getPositionRect(focusedPosition);
10511
+ // the scroll container of the editor
10512
+ const scrollContainer = editor.getScrollContainer();
10513
+ // the scrollContainer position relative to the viewport
10514
+ const scrollContainerRect = roosterjsEditorDom.normalizeRect(scrollContainer.getBoundingClientRect());
10515
+ if (focusedPosition !== null && cursorRect !== null && cursorRect !== undefined && scrollContainerRect !== null) {
10516
+ const textElement = focusedPosition.element;
10517
+ // the caret height is typically the same as the font size of the text
10518
+ const caretHeight = parseFloat(window.getComputedStyle(textElement).fontSize);
10519
+ // 1. scrollContainer.scrollTop represents the number of pixels that the content of scrollContainer is scrolled upward.
10520
+ // 2. subtract the top position of the scrollContainer element (scrollContainerRect.top) to
10521
+ // translate the scroll position from being relative to the document to being relative to the viewport.
10522
+ // 3. add the top position of the cursor (cursorRect.top) to moves the scroll position to the cursor's position.
10523
+ // 4. subtract a caret height to add some space between the cursor and the top edge of the scrollContainer.
10524
+ const updatedScrollTop = scrollContainer.scrollTop - scrollContainerRect.top + cursorRect.top - caretHeight;
10525
+ scrollContainer.scrollTo({
10526
+ top: updatedScrollTop,
10527
+ behavior: 'smooth'
10528
+ });
10529
+ }
10530
+ }
10531
+ };
10435
10532
 
10436
10533
  // Copyright (c) Microsoft Corporation.
10437
10534
  // Licensed under the MIT License.
@@ -10456,6 +10553,10 @@ const RichTextEditor = React.forwardRef((props, ref) => {
10456
10553
  if (editor.current) {
10457
10554
  editor.current.setContent('');
10458
10555
  }
10556
+ },
10557
+ getPlainContent() {
10558
+ var _a;
10559
+ return (_a = editor === null || editor === void 0 ? void 0 : editor.current) === null || _a === void 0 ? void 0 : _a.getContent(roosterjsEditorTypesCompatible.CompatibleGetContentMode.PlainTextFast);
10459
10560
  }
10460
10561
  };
10461
10562
  }, []);
@@ -10751,7 +10852,7 @@ const RichTextSendBox = (props) => {
10751
10852
  setContentValue(newValue);
10752
10853
  }, []);
10753
10854
  const sendMessageOnClick = React.useCallback(() => {
10754
- var _a, _b;
10855
+ var _a, _b, _c;
10755
10856
  if (disabled || contentValueOverflow) {
10756
10857
  return;
10757
10858
  }
@@ -10764,20 +10865,29 @@ const RichTextSendBox = (props) => {
10764
10865
  return;
10765
10866
  }
10766
10867
  const message = contentValue;
10868
+ // get plain text content from the editor to check if the message is empty
10869
+ // as the content may contain tags even when the content is empty
10870
+ const plainTextContent = (_a = editorComponentRef.current) === null || _a === void 0 ? void 0 : _a.getPlainContent();
10871
+ const hasContent = !isContentEmpty({
10872
+ plainTextContent,
10873
+ content: message,
10874
+ placeholder: strings.placeholderText
10875
+ });
10767
10876
  // we don't want to send empty messages including spaces, newlines, tabs
10768
10877
  // Message can be empty if there is a valid attachment upload
10769
- if (sanitizeText(message).length > 0 ||
10878
+ if (hasContent ||
10770
10879
  /* @conditional-compile-remove(attachment-upload) */ hasCompletedAttachmentUploads(activeAttachmentUploads)) {
10771
10880
  onSendMessage(message);
10772
10881
  setContentValue('');
10773
- (_a = editorComponentRef.current) === null || _a === void 0 ? void 0 : _a.setEmptyContent();
10882
+ (_b = editorComponentRef.current) === null || _b === void 0 ? void 0 : _b.setEmptyContent();
10774
10883
  }
10775
- (_b = editorComponentRef.current) === null || _b === void 0 ? void 0 : _b.focus();
10884
+ (_c = editorComponentRef.current) === null || _c === void 0 ? void 0 : _c.focus();
10776
10885
  }, [
10777
10886
  contentValue,
10778
10887
  contentValueOverflow,
10779
10888
  disabled,
10780
10889
  onSendMessage,
10890
+ strings.placeholderText,
10781
10891
  /* @conditional-compile-remove(attachment-upload) */ activeAttachmentUploads,
10782
10892
  /* @conditional-compile-remove(attachment-upload) */ strings.attachmentUploadsPendingError
10783
10893
  ]);
@@ -10797,15 +10907,26 @@ const RichTextSendBox = (props) => {
10797
10907
  attachmentUploadsPendingError,
10798
10908
  systemMessage
10799
10909
  ]);
10800
- const onRenderSendIcon = React.useCallback((isHover) => (React.createElement(react.Icon, { iconName: isHover && contentValue ? 'SendBoxSendHovered' : 'SendBoxSend', className: sendIconStyle({
10801
- theme,
10802
- hasText: !!contentValue,
10803
- /* @conditional-compile-remove(attachment-upload) */
10804
- hasAttachment: false,
10805
- hasErrorMessage: hasErrorMessage,
10806
- defaultTextColor: theme.palette.neutralSecondary,
10807
- disabled: disabled
10808
- }) })), [contentValue, disabled, hasErrorMessage, theme]);
10910
+ const onRenderSendIcon = React.useCallback((isHover) => {
10911
+ var _a;
10912
+ // get plain text content from the editor to check if the message is empty
10913
+ // as the content may contain tags even when the content is empty
10914
+ const plainTextContent = (_a = editorComponentRef.current) === null || _a === void 0 ? void 0 : _a.getPlainContent();
10915
+ const hasContent = !isContentEmpty({
10916
+ plainTextContent: plainTextContent,
10917
+ content: contentValue,
10918
+ placeholder: strings.placeholderText
10919
+ });
10920
+ return (React.createElement(react.Icon, { iconName: isHover && hasContent ? 'SendBoxSendHovered' : 'SendBoxSend', className: sendIconStyle({
10921
+ theme,
10922
+ hasText: hasContent,
10923
+ /* @conditional-compile-remove(attachment-upload) */
10924
+ hasAttachment: false,
10925
+ hasErrorMessage: hasErrorMessage,
10926
+ defaultTextColor: theme.palette.neutralSecondary,
10927
+ disabled: disabled
10928
+ }) }));
10929
+ }, [contentValue, disabled, hasErrorMessage, strings.placeholderText, theme]);
10809
10930
  const sendBoxErrorsProps = React.useMemo(() => {
10810
10931
  var _a;
10811
10932
  return {
@@ -10867,6 +10988,22 @@ const RichTextSendBox = (props) => {
10867
10988
  /* @conditional-compile-remove(attachment-upload) */
10868
10989
  hasAttachments: hasAttachmentUploads })));
10869
10990
  };
10991
+ /**
10992
+ * Checks if the content of the rich text editor is empty.
10993
+ *
10994
+ * @param {Object} params - The parameters for the function.
10995
+ * @param {string | undefined} params.plainTextContent - The plain text content of the editor.
10996
+ * @param {string} params.content - The HTML content of the editor.
10997
+ * @param {string} params.placeholder - The placeholder text of the editor.
10998
+ * @returns {boolean} - True if the content is empty, false otherwise.
10999
+ */
11000
+ const isContentEmpty = ({ plainTextContent, content, placeholder }) => {
11001
+ // RoosterJS returns placeholder text as plain text when the editor is empty and in this case,
11002
+ // plainTextContent contains only placeholder text but content doesn't include the placeholder text
11003
+ // this needs to be reviewed after migration to the content model packages.
11004
+ const plainTextContainsPlaceholderOnly = plainTextContent === placeholder && !content.includes(placeholder);
11005
+ return plainTextContainsPlaceholderOnly || sanitizeText(plainTextContent !== null && plainTextContent !== void 0 ? plainTextContent : '').length === 0;
11006
+ };
10870
11007
 
10871
11008
  // Copyright (c) Microsoft Corporation.
10872
11009
  // Licensed under the MIT License.
@@ -12690,7 +12827,7 @@ class ErrorBoundary extends React.Component {
12690
12827
  // Copyright (c) Microsoft Corporation.
12691
12828
  // Licensed under the MIT License.
12692
12829
  /* @conditional-compile-remove(rich-text-editor) */
12693
- const ChatMessageComponentAsRichTextEditBox = React.lazy(() => Promise.resolve().then(function () { return require('./ChatMessageComponentAsRichTextEditBox-CmrzlNzS.js'); }));
12830
+ const ChatMessageComponentAsRichTextEditBox = React.lazy(() => Promise.resolve().then(function () { return require('./ChatMessageComponentAsRichTextEditBox-C3t87AZz.js'); }));
12694
12831
  /**
12695
12832
  * @private
12696
12833
  * Use this function to load RoosterJS dependencies early in the lifecycle.
@@ -12698,7 +12835,7 @@ const ChatMessageComponentAsRichTextEditBox = React.lazy(() => Promise.resolve()
12698
12835
  *
12699
12836
  * @conditional-compile-remove(rich-text-editor)
12700
12837
  */
12701
- const loadChatMessageComponentAsRichTextEditBox = () => Promise.resolve().then(function () { return require('./ChatMessageComponentAsRichTextEditBox-CmrzlNzS.js'); });
12838
+ const loadChatMessageComponentAsRichTextEditBox = () => Promise.resolve().then(function () { return require('./ChatMessageComponentAsRichTextEditBox-C3t87AZz.js'); });
12702
12839
  /**
12703
12840
  * @private
12704
12841
  */
@@ -15582,12 +15719,12 @@ const participantsById = (participants) => {
15582
15719
  // Licensed under the MIT License.
15583
15720
  const DEFAULT_MAX_OVERFLOW_GALLERY_DOMINANT_SPEAKERS = 6;
15584
15721
  const DEFAULT_MAX_VIDEO_SREAMS = 4;
15722
+ /**
15723
+ * @private
15724
+ */
15585
15725
  const MAX_GRID_PARTICIPANTS_NOT_LARGE_GALLERY = 9;
15586
15726
  const getOrganizedParticipants = (props) => {
15587
- const { remoteParticipants = [], dominantSpeakers = [], maxRemoteVideoStreams = DEFAULT_MAX_VIDEO_SREAMS, maxOverflowGalleryDominantSpeakers = DEFAULT_MAX_OVERFLOW_GALLERY_DOMINANT_SPEAKERS, isScreenShareActive = false, layout, previousGridParticipants = [], previousOverflowParticipants = [] } = props;
15588
- const maxRemoteVideoStreamsToUse = maxRemoteVideoStreams > MAX_GRID_PARTICIPANTS_NOT_LARGE_GALLERY
15589
- ? MAX_GRID_PARTICIPANTS_NOT_LARGE_GALLERY
15590
- : maxRemoteVideoStreams;
15727
+ const { remoteParticipants = [], dominantSpeakers = [], maxGridParticipants = DEFAULT_MAX_VIDEO_SREAMS, maxOverflowGalleryDominantSpeakers = DEFAULT_MAX_OVERFLOW_GALLERY_DOMINANT_SPEAKERS, isScreenShareActive = false, layout, previousGridParticipants = [], previousOverflowParticipants = [] } = props;
15591
15728
  const remoteParticipantsOrdered = putVideoParticipantsFirst(remoteParticipants);
15592
15729
  const videoParticipants = remoteParticipants.filter((p) => { var _a; return (_a = p.videoStream) === null || _a === void 0 ? void 0 : _a.isAvailable; });
15593
15730
  const participants = layout === 'floatingLocalVideo' && videoParticipants.length > 0 ? videoParticipants : remoteParticipantsOrdered;
@@ -15595,8 +15732,8 @@ const getOrganizedParticipants = (props) => {
15595
15732
  participants: participants,
15596
15733
  dominantSpeakers,
15597
15734
  currentParticipants: previousGridParticipants,
15598
- maxDominantSpeakers: maxRemoteVideoStreamsToUse
15599
- }).slice(0, maxRemoteVideoStreamsToUse);
15735
+ maxDominantSpeakers: maxGridParticipants
15736
+ }).slice(0, maxGridParticipants);
15600
15737
  const dominantSpeakerToGrid = layout === 'speaker'
15601
15738
  ? dominantSpeakers && dominantSpeakers[0]
15602
15739
  ? newGridParticipants.filter((p) => p.userId === dominantSpeakers[0])
@@ -15621,14 +15758,14 @@ const getOrganizedParticipants = (props) => {
15621
15758
  isScreenShareActive,
15622
15759
  gridParticipants: newGridParticipants,
15623
15760
  overflowGalleryParticipants: newOverflowGalleryParticipants,
15624
- maxRemoteVideoStreams: maxRemoteVideoStreamsToUse,
15761
+ maxGridParticipants: maxGridParticipants,
15625
15762
  /* @conditional-compile-remove(PSTN-calls) */ /* @conditional-compile-remove(one-to-n-calling) */ callingParticipants
15626
15763
  });
15627
15764
  const overflowGalleryParticipants = getOverflowGalleryRemoteParticipants({
15628
15765
  isScreenShareActive,
15629
15766
  gridParticipants: newGridParticipants,
15630
15767
  overflowGalleryParticipants: newOverflowGalleryParticipants,
15631
- maxRemoteVideoStreams: maxRemoteVideoStreamsToUse,
15768
+ maxGridParticipants: maxGridParticipants,
15632
15769
  /* @conditional-compile-remove(PSTN-calls) */ /* @conditional-compile-remove(one-to-n-calling) */ callingParticipants
15633
15770
  });
15634
15771
  return { gridParticipants, overflowGalleryParticipants };
@@ -15656,7 +15793,7 @@ const useOrganizedParticipants = (props) => {
15656
15793
  const unfocusedParticipants = props.remoteParticipants.filter((p) => !focusedParticipantUserIdSet.has(p.userId));
15657
15794
  const useOrganizedParticipantsProps = Object.assign(Object.assign({}, props), {
15658
15795
  // if there are focused participants then leave no room in the grid by setting maxGridParticipants to 0
15659
- maxRemoteVideoStreams: focusedParticipants.length > 0 || props.isScreenShareActive ? 0 : props.maxRemoteVideoStreams, remoteParticipants: unfocusedParticipants, previousGridParticipants: currentGridParticipants.current, previousOverflowParticipants: currentOverflowGalleryParticipants.current });
15796
+ maxGridParticipants: focusedParticipants.length > 0 || props.isScreenShareActive ? 0 : props.maxGridParticipants, remoteParticipants: unfocusedParticipants, previousGridParticipants: currentGridParticipants.current, previousOverflowParticipants: currentOverflowGalleryParticipants.current });
15660
15797
  const useOrganizedParticipantsResult = getOrganizedParticipants(useOrganizedParticipantsProps);
15661
15798
  currentGridParticipants.current = useOrganizedParticipantsResult.gridParticipants;
15662
15799
  currentOverflowGalleryParticipants.current = useOrganizedParticipantsResult.overflowGalleryParticipants;
@@ -15679,9 +15816,9 @@ const getGridParticipants = (args) => {
15679
15816
  /* @conditional-compile-remove(PSTN-calls) */ /* @conditional-compile-remove(one-to-n-calling) */
15680
15817
  return args.gridParticipants.length > 0
15681
15818
  ? args.gridParticipants
15682
- : args.overflowGalleryParticipants.length > args.maxRemoteVideoStreams
15683
- ? args.overflowGalleryParticipants.slice(0, args.maxRemoteVideoStreams)
15684
- : args.overflowGalleryParticipants.slice(0, args.maxRemoteVideoStreams).concat(args.callingParticipants);
15819
+ : args.overflowGalleryParticipants.length > args.maxGridParticipants
15820
+ ? args.overflowGalleryParticipants.slice(0, args.maxGridParticipants)
15821
+ : args.overflowGalleryParticipants.slice(0, args.maxGridParticipants).concat(args.callingParticipants);
15685
15822
  };
15686
15823
  const getOverflowGalleryRemoteParticipants = (args) => {
15687
15824
  if (args.isScreenShareActive) {
@@ -15697,8 +15834,8 @@ const getOverflowGalleryRemoteParticipants = (args) => {
15697
15834
  /* @conditional-compile-remove(PSTN-calls) */ /* @conditional-compile-remove(one-to-n-calling) */
15698
15835
  return args.gridParticipants.length > 0
15699
15836
  ? args.overflowGalleryParticipants.concat(args.callingParticipants)
15700
- : args.overflowGalleryParticipants.length > args.maxRemoteVideoStreams
15701
- ? args.overflowGalleryParticipants.slice(args.maxRemoteVideoStreams).concat(args.callingParticipants)
15837
+ : args.overflowGalleryParticipants.length > args.maxGridParticipants
15838
+ ? args.overflowGalleryParticipants.slice(args.maxGridParticipants).concat(args.callingParticipants)
15702
15839
  : [];
15703
15840
  }
15704
15841
  };
@@ -17538,11 +17675,12 @@ const DefaultLayout = (props) => {
17538
17675
  // This number will be used for the maxOverflowGalleryDominantSpeakers when organizing the remote participants.
17539
17676
  // We need to add the local participant to the pinned participant count so we are placing the speakers correctly.
17540
17677
  const childrenPerPage = React.useRef(4);
17678
+ const remoteVideosOn = remoteParticipants.filter((p) => { var _a; return (_a = p.videoStream) === null || _a === void 0 ? void 0 : _a.isAvailable; }).length > 0;
17541
17679
  const { gridParticipants, overflowGalleryParticipants } = useOrganizedParticipants({
17542
17680
  remoteParticipants,
17543
17681
  localParticipant,
17544
17682
  dominantSpeakers,
17545
- maxRemoteVideoStreams,
17683
+ maxGridParticipants: remoteVideosOn ? maxRemoteVideoStreams : MAX_GRID_PARTICIPANTS_NOT_LARGE_GALLERY,
17546
17684
  isScreenShareActive: !!screenShareComponent,
17547
17685
  maxOverflowGalleryDominantSpeakers: screenShareComponent
17548
17686
  ? childrenPerPage.current - ((pinnedParticipantUserIds.length + 1) % childrenPerPage.current)
@@ -18370,10 +18508,11 @@ const FloatingLocalVideoLayout = (props) => {
18370
18508
  // This is for tracking the number of children in the first page of overflow gallery.
18371
18509
  // This number will be used for the maxOverflowGalleryDominantSpeakers when organizing the remote participants.
18372
18510
  const childrenPerPage = React.useRef(4);
18511
+ const remoteVideosOn = remoteParticipants.filter((p) => { var _a; return (_a = p.videoStream) === null || _a === void 0 ? void 0 : _a.isAvailable; }).length > 0;
18373
18512
  const { gridParticipants, overflowGalleryParticipants } = useOrganizedParticipants({
18374
18513
  remoteParticipants,
18375
18514
  dominantSpeakers,
18376
- maxRemoteVideoStreams,
18515
+ maxGridParticipants: remoteVideosOn ? maxRemoteVideoStreams : MAX_GRID_PARTICIPANTS_NOT_LARGE_GALLERY,
18377
18516
  isScreenShareActive: !!screenShareComponent,
18378
18517
  maxOverflowGalleryDominantSpeakers: screenShareComponent
18379
18518
  ? childrenPerPage.current - (pinnedParticipantUserIds.length % childrenPerPage.current)
@@ -18489,7 +18628,7 @@ const SpeakerVideoLayout = (props) => {
18489
18628
  const { gridParticipants, overflowGalleryParticipants } = useOrganizedParticipants({
18490
18629
  remoteParticipants,
18491
18630
  dominantSpeakers,
18492
- maxRemoteVideoStreams,
18631
+ maxGridParticipants: maxRemoteVideoStreams,
18493
18632
  isScreenShareActive: !!screenShareComponent,
18494
18633
  maxOverflowGalleryDominantSpeakers: screenShareComponent
18495
18634
  ? childrenPerPage.current - (pinnedParticipantUserIds.length % childrenPerPage.current)
@@ -18624,7 +18763,7 @@ const LargeGalleryLayout = (props) => {
18624
18763
  remoteParticipants,
18625
18764
  localParticipant,
18626
18765
  dominantSpeakers,
18627
- maxRemoteVideoStreams: maxStreamsTrampoline(),
18766
+ maxGridParticipants: maxStreamsTrampoline(),
18628
18767
  isScreenShareActive: !!screenShareComponent,
18629
18768
  maxOverflowGalleryDominantSpeakers: screenShareComponent
18630
18769
  ? childrenPerPage.current - ((pinnedParticipantUserIds.length + 1) % childrenPerPage.current)
@@ -31178,7 +31317,7 @@ const SidePaneHeader = (props) => {
31178
31317
  return React.createElement(SidePaneMobileHeader, Object.assign({}, props));
31179
31318
  }
31180
31319
  return (React.createElement(react.Stack, { horizontal: true, horizontalAlign: "space-between", styles: sidePaneHeaderContainerStyles, verticalAlign: "center" },
31181
- React.createElement(react.Stack.Item, { role: "heading", styles: sidePaneHeaderStyles }, props.headingText),
31320
+ React.createElement(react.Stack.Item, { role: "heading", styles: sidePaneHeaderStyles, "aria-label": props.headingText }, props.headingText),
31182
31321
  React.createElement(react.Stack.Item, null,
31183
31322
  React.createElement(react.CommandBarButton, { ariaLabel: props.dismissSidePaneButtonAriaLabel, styles: sidePaneCloseButtonStyles, iconProps: { iconName: 'cancel' }, onClick: props.onClose }))));
31184
31323
  };
@@ -33754,14 +33893,11 @@ const LocalDeviceSettings = (props) => {
33754
33893
  const role = (_a = adapter.getState().call) === null || _a === void 0 ? void 0 : _a.role;
33755
33894
  const cameraPermissionGranted = props.cameraPermissionGranted;
33756
33895
  const micPermissionGranted = props.microphonePermissionGranted;
33757
- let roleCanUseCamera = true;
33758
- let roleCanUseMic = true;
33759
- roleCanUseCamera = role === 'Consumer' ? false : true;
33760
- roleCanUseMic = role === 'Consumer' ? false : true;
33896
+ const roleCanUseCamera = role !== 'Consumer';
33897
+ const roleCanUseMic = role !== 'Consumer';
33761
33898
  // TODO: speaker permission is tied to microphone permission (when you request 'audio' permission using the SDK) its
33762
33899
  // actually granting access to query both microphone and speaker. However the browser popup asks you explicity for
33763
33900
  // 'microphone'. This needs investigation on how we want to handle this and maybe needs follow up with SDK team.
33764
- /* @conditional-compile-remove(call-readiness) */
33765
33901
  React.useEffect(() => {
33766
33902
  if (cameraPermissionGranted) {
33767
33903
  adapter.queryCameras();
@@ -39675,4 +39811,4 @@ exports.useTeamsCall = useTeamsCall;
39675
39811
  exports.useTeamsCallAdapter = useTeamsCallAdapter;
39676
39812
  exports.useTeamsCallAgent = useTeamsCallAgent;
39677
39813
  exports.useTheme = useTheme;
39678
- //# sourceMappingURL=index-CC43HCUd.js.map
39814
+ //# sourceMappingURL=index-BP7335hI.js.map