@azure/communication-react 1.5.1-alpha-202302230015 → 1.5.1-alpha-202303010017

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 (55) hide show
  1. package/CHANGELOG.beta.md +86 -1
  2. package/CHANGELOG.json +686 -0
  3. package/dist/communication-react.d.ts +16 -0
  4. package/dist/dist-cjs/communication-react/index.js +663 -335
  5. package/dist/dist-cjs/communication-react/index.js.map +1 -1
  6. package/dist/dist-esm/acs-ui-common/src/telemetryVersion.js +1 -1
  7. package/dist/dist-esm/acs-ui-common/src/telemetryVersion.js.map +1 -1
  8. package/dist/dist-esm/chat-stateful-client/src/EventSubscriber.js +10 -2
  9. package/dist/dist-esm/chat-stateful-client/src/EventSubscriber.js.map +1 -1
  10. package/dist/dist-esm/communication-react/src/index.d.ts +1 -0
  11. package/dist/dist-esm/communication-react/src/index.js.map +1 -1
  12. package/dist/dist-esm/react-components/src/components/HorizontalGallery.js +1 -10
  13. package/dist/dist-esm/react-components/src/components/HorizontalGallery.js.map +1 -1
  14. package/dist/dist-esm/react-components/src/components/ResponsiveVerticalGallery.d.ts +29 -0
  15. package/dist/dist-esm/react-components/src/components/ResponsiveVerticalGallery.js +77 -0
  16. package/dist/dist-esm/react-components/src/components/ResponsiveVerticalGallery.js.map +1 -0
  17. package/dist/dist-esm/react-components/src/components/VerticalGallery.d.ts +50 -0
  18. package/dist/dist-esm/react-components/src/components/VerticalGallery.js +85 -0
  19. package/dist/dist-esm/react-components/src/components/VerticalGallery.js.map +1 -0
  20. package/dist/dist-esm/react-components/src/components/VideoGallery/DefaultLayout.js +19 -13
  21. package/dist/dist-esm/react-components/src/components/VideoGallery/DefaultLayout.js.map +1 -1
  22. package/dist/dist-esm/react-components/src/components/VideoGallery/FloatingLocalVideo.d.ts +10 -3
  23. package/dist/dist-esm/react-components/src/components/VideoGallery/FloatingLocalVideo.js +6 -8
  24. package/dist/dist-esm/react-components/src/components/VideoGallery/FloatingLocalVideo.js.map +1 -1
  25. package/dist/dist-esm/react-components/src/components/VideoGallery/FloatingLocalVideoLayout.js +32 -16
  26. package/dist/dist-esm/react-components/src/components/VideoGallery/FloatingLocalVideoLayout.js.map +1 -1
  27. package/dist/dist-esm/react-components/src/components/VideoGallery/Layout.d.ts +6 -0
  28. package/dist/dist-esm/react-components/src/components/VideoGallery/Layout.js.map +1 -1
  29. package/dist/dist-esm/react-components/src/components/VideoGallery/{VideoGalleryResponsiveHorizontalGallery.d.ts → OverflowGallery.d.ts} +5 -3
  30. package/dist/dist-esm/react-components/src/components/VideoGallery/OverflowGallery.js +46 -0
  31. package/dist/dist-esm/react-components/src/components/VideoGallery/OverflowGallery.js.map +1 -0
  32. package/dist/dist-esm/react-components/src/components/VideoGallery/styles/FloatingLocalVideo.styles.d.ts +20 -3
  33. package/dist/dist-esm/react-components/src/components/VideoGallery/styles/FloatingLocalVideo.styles.js +11 -6
  34. package/dist/dist-esm/react-components/src/components/VideoGallery/styles/FloatingLocalVideo.styles.js.map +1 -1
  35. package/dist/dist-esm/react-components/src/components/VideoGallery/styles/VideoGalleryResponsiveVerticalGallery.styles.d.ts +38 -0
  36. package/dist/dist-esm/react-components/src/components/VideoGallery/styles/VideoGalleryResponsiveVerticalGallery.styles.js +44 -0
  37. package/dist/dist-esm/react-components/src/components/VideoGallery/styles/VideoGalleryResponsiveVerticalGallery.styles.js.map +1 -0
  38. package/dist/dist-esm/react-components/src/components/VideoGallery.d.ts +11 -0
  39. package/dist/dist-esm/react-components/src/components/VideoGallery.js +7 -3
  40. package/dist/dist-esm/react-components/src/components/VideoGallery.js.map +1 -1
  41. package/dist/dist-esm/react-components/src/components/index.d.ts +1 -0
  42. package/dist/dist-esm/react-components/src/components/index.js.map +1 -1
  43. package/dist/dist-esm/react-components/src/components/styles/VerticalGallery.styles.d.ts +32 -0
  44. package/dist/dist-esm/react-components/src/components/styles/VerticalGallery.styles.js +63 -0
  45. package/dist/dist-esm/react-components/src/components/styles/VerticalGallery.styles.js.map +1 -0
  46. package/dist/dist-esm/react-components/src/components/utils/overFlowGalleriesUtils.d.ts +11 -0
  47. package/dist/dist-esm/react-components/src/components/utils/overFlowGalleriesUtils.js +22 -0
  48. package/dist/dist-esm/react-components/src/components/utils/overFlowGalleriesUtils.js.map +1 -0
  49. package/dist/dist-esm/react-components/src/theming/icons.d.ts +2 -0
  50. package/dist/dist-esm/react-components/src/theming/icons.js +5 -1
  51. package/dist/dist-esm/react-components/src/theming/icons.js.map +1 -1
  52. package/dist/dist-esm/react-composites/src/composites/common/icons.d.ts +2 -0
  53. package/package.json +10 -10
  54. package/dist/dist-esm/react-components/src/components/VideoGallery/VideoGalleryResponsiveHorizontalGallery.js +0 -19
  55. package/dist/dist-esm/react-components/src/components/VideoGallery/VideoGalleryResponsiveHorizontalGallery.js.map +0 -1
@@ -161,7 +161,7 @@ const _toCommunicationIdentifier = (id) => {
161
161
  // Copyright (c) Microsoft Corporation.
162
162
  // Licensed under the MIT license.
163
163
  // GENERATED FILE. DO NOT EDIT MANUALLY.
164
- var telemetryVersion = '1.5.1-alpha-202302230015';
164
+ var telemetryVersion = '1.5.1-alpha-202303010017';
165
165
 
166
166
  // Copyright (c) Microsoft Corporation.
167
167
  /**
@@ -5364,7 +5364,11 @@ const DEFAULT_COMPONENT_ICONS = {
5364
5364
  /* @conditional-compile-remove(pinned-participants) */
5365
5365
  PinParticipant: React__default['default'].createElement(reactIcons.Pin16Regular, null),
5366
5366
  /* @conditional-compile-remove(pinned-participants) */
5367
- UnpinParticipant: React__default['default'].createElement(reactIcons.PinOff16Regular, null)
5367
+ UnpinParticipant: React__default['default'].createElement(reactIcons.PinOff16Regular, null),
5368
+ /* @conditional-compile-remove(vertical-gallery) */
5369
+ VerticalGalleryLeftButton: React__default['default'].createElement(reactIcons.ChevronLeft20Regular, null),
5370
+ /* @conditional-compile-remove(vertical-gallery) */
5371
+ VerticalGalleryRightButton: React__default['default'].createElement(reactIcons.ChevronRight20Regular, null)
5368
5372
  };
5369
5373
 
5370
5374
  // Copyright (c) Microsoft Corporation.
@@ -9199,176 +9203,6 @@ const FloatingLocalCameraCycleButton = (props) => {
9199
9203
  (localVideoCameraCycleButtonProps === null || localVideoCameraCycleButtonProps === void 0 ? void 0 : localVideoCameraCycleButtonProps.onSelectCamera) !== undefined && (React__default['default'].createElement(LocalVideoCameraCycleButton, { cameras: localVideoCameraCycleButtonProps.cameras, selectedCamera: localVideoCameraCycleButtonProps.selectedCamera, onSelectCamera: localVideoCameraCycleButtonProps.onSelectCamera, label: localVideoCameraSwitcherLabel, ariaDescription: ariaDescription }))));
9200
9204
  };
9201
9205
 
9202
- // Copyright (c) Microsoft Corporation.
9203
- /**
9204
- * @private
9205
- */
9206
- react.mergeStyles({ position: 'relative', width: '100%', height: '100%' });
9207
- /**
9208
- * Small floating modal width and height in rem for small screen
9209
- */
9210
- const SMALL_FLOATING_MODAL_SIZE_PX = { width: 58, height: 104 };
9211
- /**
9212
- * Large floating modal width and height in rem for large screen
9213
- * Aspect ratio: 16:9
9214
- */
9215
- const LARGE_FLOATING_MODAL_SIZE_PX = { width: 215, height: 120 };
9216
- /**
9217
- * @private
9218
- * z-index to ensure that the local video tile is above the video gallery.
9219
- */
9220
- const LOCAL_VIDEO_TILE_ZINDEX = 2;
9221
- /**
9222
- * @private
9223
- */
9224
- const localVideoTileContainerStyle = (theme, isNarrow) => {
9225
- return Object.assign({ minWidth: isNarrow ? _pxToRem(SMALL_FLOATING_MODAL_SIZE_PX.width) : _pxToRem(LARGE_FLOATING_MODAL_SIZE_PX.width), minHeight: isNarrow ? _pxToRem(SMALL_FLOATING_MODAL_SIZE_PX.height) : _pxToRem(LARGE_FLOATING_MODAL_SIZE_PX.height), position: 'absolute', bottom: _pxToRem(localVideoTileOuterPaddingPX), borderRadius: theme.effects.roundedCorner4, overflow: 'hidden' }, (theme.rtl
9226
- ? { left: _pxToRem(localVideoTileOuterPaddingPX) }
9227
- : { right: _pxToRem(localVideoTileOuterPaddingPX) }));
9228
- };
9229
- /**
9230
- * @private
9231
- */
9232
- const localVideoTileWithControlsContainerStyle = (theme, isNarrow) => {
9233
- return react.concatStyleSets(localVideoTileContainerStyle(theme, isNarrow), {
9234
- root: { boxShadow: theme.effects.elevation8 }
9235
- });
9236
- };
9237
- /**
9238
- * @private
9239
- */
9240
- const floatingLocalVideoModalStyle = (theme, isNarrow) => {
9241
- return react.concatStyleSets({
9242
- main: localVideoTileContainerStyle(theme, isNarrow)
9243
- }, {
9244
- main: {
9245
- boxShadow: theme.effects.elevation8,
9246
- ':focus-within': {
9247
- boxShadow: theme.effects.elevation16,
9248
- border: `${_pxToRem(2)} solid ${theme.palette.neutralPrimary}`
9249
- }
9250
- }
9251
- }, localVideoModalStyles);
9252
- };
9253
- /**
9254
- * Padding equal to the amount the modal should stay inside the bounds of the container.
9255
- * i.e. if this is 8px, the modal should always be at least 8px inside the container at all times on all sides.
9256
- * @private
9257
- */
9258
- const localVideoTileOuterPaddingPX = 8;
9259
- /**
9260
- * @private
9261
- */
9262
- const floatingLocalVideoTileStyle = {
9263
- root: {
9264
- position: 'absolute',
9265
- zIndex: LOCAL_VIDEO_TILE_ZINDEX,
9266
- height: '100%',
9267
- width: '100%'
9268
- }
9269
- };
9270
- /**
9271
- * Styles for the local video tile modal when it is focused, will cause keyboard move icon to appear over video
9272
- * @private
9273
- */
9274
- const localVideoModalStyles = {
9275
- keyboardMoveIconContainer: {
9276
- zIndex: LOCAL_VIDEO_TILE_ZINDEX + 1 // zIndex to set the keyboard movement Icon above the other layers in the video tile.
9277
- }
9278
- };
9279
-
9280
- // Copyright (c) Microsoft Corporation.
9281
- /**
9282
- * @private
9283
- */
9284
- const horizontalGalleryContainerStyle = (shouldFloatLocalVideo, isNarrow) => {
9285
- return {
9286
- minHeight: isNarrow
9287
- ? `${SMALL_HORIZONTAL_GALLERY_TILE_SIZE_REM.height}rem`
9288
- : `${LARGE_HORIZONTAL_GALLERY_TILE_SIZE_REM.height}rem`,
9289
- width: shouldFloatLocalVideo
9290
- ? isNarrow
9291
- ? `calc(100% - ${_pxToRem(SMALL_FLOATING_MODAL_SIZE_PX.width)})`
9292
- : `calc(100% - ${_pxToRem(LARGE_FLOATING_MODAL_SIZE_PX.width)})`
9293
- : '100%',
9294
- paddingRight: '0.5rem'
9295
- };
9296
- };
9297
- /**
9298
- * @private
9299
- */
9300
- const horizontalGalleryStyle = (isNarrow) => {
9301
- return {
9302
- children: isNarrow ? SMALL_HORIZONTAL_GALLERY_TILE_STYLE : LARGE_HORIZONTAL_GALLERY_TILE_STYLE
9303
- };
9304
- };
9305
- /**
9306
- * Small horizontal gallery tile size in rem
9307
- * @private
9308
- */
9309
- const SMALL_HORIZONTAL_GALLERY_TILE_SIZE_REM = { height: 6.5, width: 6.5 };
9310
- /**
9311
- * Large horizontal gallery tile size in rem
9312
- * @private
9313
- */
9314
- const LARGE_HORIZONTAL_GALLERY_TILE_SIZE_REM = { height: 7.5, width: 10 };
9315
- /**
9316
- * @private
9317
- */
9318
- const SMALL_HORIZONTAL_GALLERY_TILE_STYLE = {
9319
- minHeight: `${SMALL_HORIZONTAL_GALLERY_TILE_SIZE_REM.height}rem`,
9320
- minWidth: `${SMALL_HORIZONTAL_GALLERY_TILE_SIZE_REM.width}rem`,
9321
- maxHeight: `${SMALL_HORIZONTAL_GALLERY_TILE_SIZE_REM.height}rem`,
9322
- maxWidth: `${SMALL_HORIZONTAL_GALLERY_TILE_SIZE_REM.width}rem`
9323
- };
9324
- /**
9325
- * @private
9326
- */
9327
- const LARGE_HORIZONTAL_GALLERY_TILE_STYLE = {
9328
- minHeight: `${LARGE_HORIZONTAL_GALLERY_TILE_SIZE_REM.height}rem`,
9329
- minWidth: `${LARGE_HORIZONTAL_GALLERY_TILE_SIZE_REM.width}rem`,
9330
- maxHeight: `${LARGE_HORIZONTAL_GALLERY_TILE_SIZE_REM.height}rem`,
9331
- maxWidth: `${LARGE_HORIZONTAL_GALLERY_TILE_SIZE_REM.width}rem`
9332
- };
9333
-
9334
- // Copyright (c) Microsoft Corporation.
9335
- /**
9336
- * @private
9337
- */
9338
- const scrollableHorizontalGalleryStyles = {
9339
- root: {
9340
- width: '100%',
9341
- minHeight: `${SMALL_HORIZONTAL_GALLERY_TILE_SIZE_REM.height}rem`,
9342
- paddingRight: '0.5rem',
9343
- '> *': SMALL_HORIZONTAL_GALLERY_TILE_STYLE
9344
- }
9345
- };
9346
- /**
9347
- * @private
9348
- */
9349
- const scrollableHorizontalGalleryContainerStyles = react.mergeStyles({
9350
- display: 'flex',
9351
- width: `calc(100% - ${SMALL_FLOATING_MODAL_SIZE_PX.width}px)`,
9352
- minHeight: `${SMALL_HORIZONTAL_GALLERY_TILE_SIZE_REM.height}rem`,
9353
- overflow: 'scroll',
9354
- '-ms-overflow-style': 'none',
9355
- 'scrollbar-width': 'none',
9356
- '::-webkit-scrollbar': { display: 'none' }
9357
- });
9358
-
9359
- // Copyright (c) Microsoft Corporation.
9360
- /**
9361
- * Component to display elements horizontally in a scrollable container
9362
- * @private
9363
- */
9364
- const ScrollableHorizontalGallery = (props) => {
9365
- const { horizontalGalleryElements } = props;
9366
- const ref = React.useRef();
9367
- const { events: dragabbleEvents } = reactUseDraggableScroll.useDraggable(ref);
9368
- return (React__default['default'].createElement("div", Object.assign({ ref: ref }, dragabbleEvents, { className: scrollableHorizontalGalleryContainerStyles }),
9369
- React__default['default'].createElement(react.Stack, { "data-ui-id": "scrollable-horizontal-gallery", horizontal: true, styles: scrollableHorizontalGalleryStyles, tokens: { childrenGap: '0.5rem' } }, horizontalGalleryElements)));
9370
- };
9371
-
9372
9206
  // Copyright (c) Microsoft Corporation.
9373
9207
  // Licensed under the MIT license.
9374
9208
  /**
@@ -9533,168 +9367,608 @@ const _useOrganizedParticipantsWithPinnedParticipants = (props) => {
9533
9367
  if (pinnedParticipants.length === 0) {
9534
9368
  return useOrganizedParticipantsResult;
9535
9369
  }
9536
- return {
9537
- gridParticipants: props.isScreenShareActive ? [] : pinnedParticipants,
9538
- horizontalGalleryParticipants: props.isScreenShareActive
9539
- ? pinnedParticipants.concat(useOrganizedParticipantsResult.horizontalGalleryParticipants)
9540
- : useOrganizedParticipantsResult.gridParticipants.concat(useOrganizedParticipantsResult.horizontalGalleryParticipants)
9541
- };
9370
+ return {
9371
+ gridParticipants: props.isScreenShareActive ? [] : pinnedParticipants,
9372
+ horizontalGalleryParticipants: props.isScreenShareActive
9373
+ ? pinnedParticipants.concat(useOrganizedParticipantsResult.horizontalGalleryParticipants)
9374
+ : useOrganizedParticipantsResult.gridParticipants.concat(useOrganizedParticipantsResult.horizontalGalleryParticipants)
9375
+ };
9376
+ };
9377
+ /**
9378
+ * Hook to determine which participants should be in grid and horizontal gallery and their order respectively
9379
+ * @private
9380
+ */
9381
+ const useOrganizedParticipants = (args) => {
9382
+ /* @conditional-compile-remove(pinned-participants) */
9383
+ return _useOrganizedParticipantsWithPinnedParticipants(args);
9384
+ };
9385
+
9386
+ // Copyright (c) Microsoft Corporation.
9387
+ // Licensed under the MIT license.
9388
+ /**
9389
+ * Horizontal Gallery button width in rem
9390
+ */
9391
+ const HORIZONTAL_GALLERY_BUTTON_WIDTH = 1.75;
9392
+ /**
9393
+ * @private
9394
+ */
9395
+ const leftRightButtonStyles$1 = (theme) => {
9396
+ return {
9397
+ background: 'none',
9398
+ padding: 0,
9399
+ height: 'auto',
9400
+ minWidth: `${HORIZONTAL_GALLERY_BUTTON_WIDTH}rem`,
9401
+ maxWidth: `${HORIZONTAL_GALLERY_BUTTON_WIDTH}rem`,
9402
+ border: `1px solid ${theme.palette.neutralLight}`,
9403
+ borderRadius: theme.effects.roundedCorner4
9404
+ };
9405
+ };
9406
+ /**
9407
+ * Horizontal Gallery gap size in rem between tiles and buttons
9408
+ */
9409
+ const HORIZONTAL_GALLERY_GAP = 0.5;
9410
+ /**
9411
+ * @private
9412
+ */
9413
+ const rootStyle$1 = {
9414
+ height: '100%',
9415
+ width: '100%',
9416
+ gap: `${HORIZONTAL_GALLERY_GAP}rem`
9417
+ };
9418
+ /**
9419
+ * @private
9420
+ */
9421
+ const childrenContainerStyle$1 = {
9422
+ height: '100%',
9423
+ gap: `${HORIZONTAL_GALLERY_GAP}rem`
9424
+ };
9425
+
9426
+ // Copyright (c) Microsoft Corporation.
9427
+ // Licensed under the MIT license.
9428
+ /**
9429
+ * Helper function to bucketize a given array of items into buckets of a specified size.
9430
+ *
9431
+ * @param arr array to bucketize
9432
+ * @param bucketSize number of children for each bucket
9433
+ * @returns nested array of given children
9434
+ *
9435
+ * @private
9436
+ */
9437
+ function bucketize(arr, bucketSize) {
9438
+ const bucketArray = [];
9439
+ if (bucketSize <= 0) {
9440
+ return bucketArray;
9441
+ }
9442
+ for (let i = 0; i < arr.length; i += bucketSize) {
9443
+ bucketArray.push(arr.slice(i, i + bucketSize));
9444
+ }
9445
+ return bucketArray;
9446
+ }
9447
+
9448
+ // Copyright (c) Microsoft Corporation.
9449
+ /**
9450
+ * {@link HorizontalGallery} default children per page
9451
+ */
9452
+ const DEFAULT_CHILDREN_PER_PAGE = 5;
9453
+ /**
9454
+ * Renders a horizontal gallery that parents children horizontally. Handles pagination based on the childrenPerPage prop.
9455
+ * @param props - HorizontalGalleryProps {@link @azure/communication-react#HorizontalGalleryProps}
9456
+ * @returns
9457
+ */
9458
+ const HorizontalGallery = (props) => {
9459
+ var _a, _b;
9460
+ const { children, childrenPerPage = DEFAULT_CHILDREN_PER_PAGE, styles } = props;
9461
+ const ids = useIdentifiers();
9462
+ const [page, setPage] = React.useState(0);
9463
+ const numberOfChildren = React__default['default'].Children.count(children);
9464
+ const lastPage = Math.ceil(numberOfChildren / childrenPerPage) - 1;
9465
+ const paginatedChildren = React.useMemo(() => {
9466
+ return bucketize(React__default['default'].Children.toArray(children), childrenPerPage);
9467
+ }, [children, childrenPerPage]);
9468
+ // If children per page is 0 or less return empty element
9469
+ if (childrenPerPage <= 0) {
9470
+ return React__default['default'].createElement(React__default['default'].Fragment, null);
9471
+ }
9472
+ const firstIndexOfCurrentPage = page * childrenPerPage;
9473
+ const clippedPage = firstIndexOfCurrentPage < numberOfChildren - 1 ? page : lastPage;
9474
+ const childrenOnCurrentPage = paginatedChildren[clippedPage];
9475
+ const showButtons = numberOfChildren > childrenPerPage;
9476
+ const disablePreviousButton = page === 0;
9477
+ const disableNextButton = page === lastPage;
9478
+ return (React__default['default'].createElement(react.Stack, { horizontal: true, className: react.mergeStyles(rootStyle$1, (_a = props.styles) === null || _a === void 0 ? void 0 : _a.root) },
9479
+ showButtons && (React__default['default'].createElement(HorizontalGalleryNavigationButton, { key: "previous-nav-button", icon: React__default['default'].createElement(react.Icon, { iconName: "HorizontalGalleryLeftButton" }), styles: styles === null || styles === void 0 ? void 0 : styles.previousButton, onClick: () => setPage(Math.max(0, Math.min(lastPage, page - 1))), disabled: disablePreviousButton, identifier: ids.horizontalGalleryLeftNavButton })),
9480
+ React__default['default'].createElement(react.Stack, { horizontal: true, className: react.mergeStyles(childrenContainerStyle$1, { '> *': (_b = props.styles) === null || _b === void 0 ? void 0 : _b.children }) }, childrenOnCurrentPage),
9481
+ showButtons && (React__default['default'].createElement(HorizontalGalleryNavigationButton, { key: "next-nav-button", icon: React__default['default'].createElement(react.Icon, { iconName: "HorizontalGalleryRightButton" }), styles: styles === null || styles === void 0 ? void 0 : styles.nextButton, onClick: () => setPage(Math.min(lastPage, page + 1)), disabled: disableNextButton, identifier: ids.horizontalGalleryRightNavButton }))));
9482
+ };
9483
+ const HorizontalGalleryNavigationButton = (props) => {
9484
+ const theme = useTheme();
9485
+ return (React__default['default'].createElement(react.DefaultButton, { className: react.mergeStyles(leftRightButtonStyles$1(theme), props.styles), onClick: props.onClick, disabled: props.disabled, "data-ui-id": props.identifier }, props.icon));
9486
+ };
9487
+
9488
+ // Copyright (c) Microsoft Corporation.
9489
+ /**
9490
+ * Wrapped HorizontalGallery that adjusts the number of items per page based on the
9491
+ * available width obtained from a ResizeObserver, width per child, gap width, and button width
9492
+ */
9493
+ const ResponsiveHorizontalGallery = (props) => {
9494
+ const { childWidthRem, gapWidthRem, buttonWidthRem = 0 } = props;
9495
+ const containerRef = React.useRef(null);
9496
+ const containerWidth = _useContainerWidth(containerRef);
9497
+ const leftPadding = containerRef.current ? parseFloat(getComputedStyle(containerRef.current).paddingLeft) : 0;
9498
+ const rightPadding = containerRef.current ? parseFloat(getComputedStyle(containerRef.current).paddingRight) : 0;
9499
+ const childrenPerPage = calculateChildrenPerPage$1({
9500
+ numberOfChildren: React__default['default'].Children.count(props.children),
9501
+ containerWidth: (containerWidth !== null && containerWidth !== void 0 ? containerWidth : 0) - leftPadding - rightPadding,
9502
+ childWidthRem,
9503
+ gapWidthRem,
9504
+ buttonWidthRem
9505
+ });
9506
+ return (React__default['default'].createElement("div", { ref: containerRef, className: react.mergeStyles(props.containerStyles) },
9507
+ React__default['default'].createElement(HorizontalGallery, { childrenPerPage: childrenPerPage, styles: props.horizontalGalleryStyles }, props.children)));
9508
+ };
9509
+ /**
9510
+ * Helper function to calculate children per page for HorizontalGallery based on width of container, child, buttons, and
9511
+ * gaps in between
9512
+ */
9513
+ const calculateChildrenPerPage$1 = (args) => {
9514
+ const { numberOfChildren, containerWidth, buttonWidthRem, childWidthRem, gapWidthRem } = args;
9515
+ const childWidth = _convertRemToPx(childWidthRem);
9516
+ const gapWidth = _convertRemToPx(gapWidthRem);
9517
+ /** First check how many children can fit in containerWidth.
9518
+ * __________________________________
9519
+ * | || |
9520
+ * | || |
9521
+ * |________________||________________|
9522
+ * <-----------containerWidth--------->
9523
+ * containerWidth = n * childWidth + (n - 1) * gapWidth. Isolate n and take the floor.
9524
+ */
9525
+ const numberOfChildrenInContainer = Math.floor((containerWidth + gapWidth) / (childWidth + gapWidth));
9526
+ // If all children fit then return numberOfChildrenInContainer
9527
+ if (numberOfChildren <= numberOfChildrenInContainer) {
9528
+ return numberOfChildrenInContainer;
9529
+ }
9530
+ const buttonWidth = _convertRemToPx(buttonWidthRem);
9531
+ /** We know we need to paginate. So we need to subtract the buttonWidth twice and gapWidth twice from
9532
+ * containerWidth to compute childrenSpace
9533
+ * <-----------containerWidth--------->
9534
+ * __________________________________
9535
+ * | || || || |
9536
+ * |<|| || ||>|
9537
+ * |_||_____________||_____________||_|
9538
+ * <-------childrenSpace------>
9539
+ */
9540
+ const childrenSpace = containerWidth - 2 * buttonWidth - 2 * gapWidth;
9541
+ // Now that we have childrenSpace width we can figure out how many children can fit in childrenSpace.
9542
+ // childrenSpace = n * childWidth + (n - 1) * gapWidth. Isolate n and take the floor.
9543
+ return Math.floor((childrenSpace + gapWidth) / (childWidth + gapWidth));
9544
+ };
9545
+
9546
+ // Copyright (c) Microsoft Corporation.
9547
+ // Licensed under the MIT license.
9548
+ /**
9549
+ * Vertical Gallery gap size in rem between tiles and buttons
9550
+ *
9551
+ * @private
9552
+ */
9553
+ const VERTICAL_GALLERY_GAP = 0.5;
9554
+ /**
9555
+ * @private
9556
+ */
9557
+ const childrenContainerStyle = {
9558
+ width: '100%',
9559
+ gap: `${VERTICAL_GALLERY_GAP}rem`
9560
+ };
9561
+ /**
9562
+ * @private
9563
+ */
9564
+ const rootStyle = {
9565
+ height: '100%',
9566
+ width: '100%',
9567
+ gap: `${VERTICAL_GALLERY_GAP}rem`,
9568
+ position: 'relative'
9569
+ };
9570
+ /**
9571
+ * @private
9572
+ */
9573
+ const pageNavigationControlBarContainerStyle = {
9574
+ height: '2rem',
9575
+ width: '100%',
9576
+ position: 'absolute',
9577
+ bottom: '0'
9578
+ };
9579
+ /**
9580
+ * @private
9581
+ */
9582
+ const leftRightButtonStyles = (theme) => {
9583
+ return {
9584
+ background: 'none',
9585
+ padding: 0,
9586
+ height: 'auto',
9587
+ borderRadius: theme.effects.roundedCorner4,
9588
+ border: 'none',
9589
+ minWidth: '2rem'
9590
+ };
9591
+ };
9592
+ /**
9593
+ * @private
9594
+ */
9595
+ const participantPageCounter = {
9596
+ lineHeight: '2rem',
9597
+ width: '100%',
9598
+ textAlign: 'center'
9599
+ };
9600
+ /**
9601
+ * @private
9602
+ */
9603
+ const navIconStyles = {
9604
+ root: {
9605
+ lineHeight: '0'
9606
+ }
9607
+ };
9608
+
9609
+ // Copyright (c) Microsoft Corporation.
9610
+ /**
9611
+ * VerticalGallery is a overflow gallery for participants in the {@link VideoGallery} component. Stacks
9612
+ * participants on the Y-axis of the VideoGallery for better use of horizontal space.
9613
+ *
9614
+ * @beta
9615
+ */
9616
+ const VerticalGallery = (props) => {
9617
+ const { children, styles, childrenPerPage } = props;
9618
+ const [page, setPage] = React.useState(0);
9619
+ const [buttonState, setButtonState] = React.useState({ previous: true, next: true });
9620
+ const numberOfChildren = React__default['default'].Children.count(children);
9621
+ const lastPage = Math.ceil(numberOfChildren / childrenPerPage) - 1;
9622
+ const paginatedChildren = React.useMemo(() => {
9623
+ return bucketize(React__default['default'].Children.toArray(children), childrenPerPage);
9624
+ }, [children, childrenPerPage]);
9625
+ const firstIndexOfCurrentPage = page * childrenPerPage;
9626
+ const clippedPage = firstIndexOfCurrentPage < numberOfChildren - 1 ? page : lastPage;
9627
+ const childrenOnCurrentPage = paginatedChildren[clippedPage];
9628
+ const showButtons = numberOfChildren > childrenPerPage;
9629
+ const onPreviousButtonClick = () => {
9630
+ setPage(page - 1);
9631
+ };
9632
+ const onNextButtonClick = () => {
9633
+ setPage(page + 1);
9634
+ };
9635
+ React.useEffect(() => {
9636
+ if (page > 0 && page < lastPage && showButtons) {
9637
+ // we are somewhere in between first and last pages.
9638
+ setButtonState({ previous: false, next: false });
9639
+ }
9640
+ else if (page === 0 && showButtons) {
9641
+ // we are on the first page.
9642
+ setButtonState({ previous: true, next: false });
9643
+ }
9644
+ else if (page === lastPage && showButtons) {
9645
+ // we are on the last page.
9646
+ setButtonState({ previous: false, next: true });
9647
+ }
9648
+ }, [page, numberOfChildren, lastPage, showButtons]);
9649
+ const childContainerStyle = React.useMemo(() => {
9650
+ return { root: childrenContainerStyle };
9651
+ }, []);
9652
+ const childrenStyles = React.useMemo(() => {
9653
+ return { root: styles === null || styles === void 0 ? void 0 : styles.children };
9654
+ }, [styles === null || styles === void 0 ? void 0 : styles.children]);
9655
+ if (childrenPerPage <= 0) {
9656
+ return React__default['default'].createElement(React__default['default'].Fragment, null);
9657
+ }
9658
+ return (React__default['default'].createElement(react.Stack, { className: react.mergeStyles(rootStyle, styles === null || styles === void 0 ? void 0 : styles.root) },
9659
+ React__default['default'].createElement(react.Stack, { styles: childContainerStyle }, childrenOnCurrentPage.map((child, i) => {
9660
+ return (React__default['default'].createElement(react.Stack.Item, { key: i, styles: childrenStyles }, child));
9661
+ })),
9662
+ showButtons && (React__default['default'].createElement(VerticalGalleryControlBar, { buttonsDisabled: buttonState, onPreviousButtonClick: onPreviousButtonClick, onNextButtonClick: onNextButtonClick, totalPages: lastPage, currentPage: page }))));
9663
+ };
9664
+ const VerticalGalleryControlBar = (props) => {
9665
+ const { onNextButtonClick, onPreviousButtonClick, buttonsDisabled, currentPage, totalPages, styles } = props;
9666
+ const theme = useTheme();
9667
+ const pageCounterContainerStyles = React.useMemo(() => {
9668
+ return react.mergeStyles(pageNavigationControlBarContainerStyle, styles === null || styles === void 0 ? void 0 : styles.root);
9669
+ }, [styles === null || styles === void 0 ? void 0 : styles.root]);
9670
+ const previousButtonSyles = React.useMemo(() => {
9671
+ return react.mergeStyles(leftRightButtonStyles(theme), styles === null || styles === void 0 ? void 0 : styles.previousButton);
9672
+ }, [styles === null || styles === void 0 ? void 0 : styles.previousButton, theme]);
9673
+ const pageCounterStyles = React.useMemo(() => {
9674
+ return react.mergeStyles(participantPageCounter, styles === null || styles === void 0 ? void 0 : styles.counter);
9675
+ }, [styles === null || styles === void 0 ? void 0 : styles.counter]);
9676
+ const nextButtonsStyles = React.useMemo(() => {
9677
+ return react.mergeStyles(leftRightButtonStyles(theme), styles === null || styles === void 0 ? void 0 : styles.nextButton);
9678
+ }, [styles === null || styles === void 0 ? void 0 : styles.nextButton, theme]);
9679
+ const controlBarSpacing = { childrenGap: '0.5rem' };
9680
+ return (React__default['default'].createElement(react.Stack, { horizontalAlign: "center", tokens: controlBarSpacing, horizontal: true, className: pageCounterContainerStyles },
9681
+ React__default['default'].createElement(react.DefaultButton, { className: previousButtonSyles, onClick: onPreviousButtonClick, disabled: buttonsDisabled === null || buttonsDisabled === void 0 ? void 0 : buttonsDisabled.previous },
9682
+ React__default['default'].createElement(react.Icon, { iconName: "VerticalGalleryLeftButton", styles: navIconStyles })),
9683
+ React__default['default'].createElement(react.Text, { className: pageCounterStyles }, `${currentPage} / ${totalPages}`),
9684
+ React__default['default'].createElement(react.DefaultButton, { className: nextButtonsStyles, onClick: onNextButtonClick, disabled: buttonsDisabled === null || buttonsDisabled === void 0 ? void 0 : buttonsDisabled.next },
9685
+ React__default['default'].createElement(react.Icon, { iconName: "VerticalGalleryRightButton", styles: navIconStyles }))));
9686
+ };
9687
+
9688
+ // Copyright (c) Microsoft Corporation.
9689
+ /**
9690
+ * @private
9691
+ */
9692
+ react.mergeStyles({ position: 'relative', width: '100%', height: '100%' });
9693
+ /**
9694
+ * Small floating modal width and height in rem for small screen
9695
+ */
9696
+ const SMALL_FLOATING_MODAL_SIZE_PX = { width: 58, height: 104 };
9697
+ /**
9698
+ * Large floating modal width and height in rem for large screen
9699
+ * Aspect ratio: 16:9
9700
+ */
9701
+ const LARGE_FLOATING_MODAL_SIZE_PX = { width: 215, height: 120 };
9702
+ /**
9703
+ * Vertical gallery floating modal width and height in rem
9704
+ * Aspect ratio: 16:9
9705
+ */
9706
+ const VERTICAL_GALLERY_FLOATING_MODAL_SIZE_PX = { width: 144, height: 81 };
9707
+ /**
9708
+ * @private
9709
+ * z-index to ensure that the local video tile is above the video gallery.
9710
+ */
9711
+ const LOCAL_VIDEO_TILE_ZINDEX = 2;
9712
+ /**
9713
+ * @private
9714
+ */
9715
+ const localVideoTileContainerStyle = (theme, localVideoTileSize) => {
9716
+ return Object.assign({ minWidth: _pxToRem(localVideoTileSize.width), minHeight: _pxToRem(localVideoTileSize.height), position: 'absolute', bottom: _pxToRem(localVideoTileOuterPaddingPX), borderRadius: theme.effects.roundedCorner4, overflow: 'hidden' }, (theme.rtl
9717
+ ? { left: _pxToRem(localVideoTileOuterPaddingPX) }
9718
+ : { right: _pxToRem(localVideoTileOuterPaddingPX) }));
9719
+ };
9720
+ /**
9721
+ * @private
9722
+ */
9723
+ const localVideoTileWithControlsContainerStyle = (theme, localVideoTileSize) => {
9724
+ return react.concatStyleSets(localVideoTileContainerStyle(theme, localVideoTileSize), {
9725
+ root: { boxShadow: theme.effects.elevation8 }
9726
+ });
9727
+ };
9728
+ /**
9729
+ * @private
9730
+ */
9731
+ const floatingLocalVideoModalStyle = (theme, modalSize) => {
9732
+ return react.concatStyleSets({
9733
+ main: localVideoTileContainerStyle(theme, modalSize)
9734
+ }, {
9735
+ main: {
9736
+ boxShadow: theme.effects.elevation8,
9737
+ ':focus-within': {
9738
+ boxShadow: theme.effects.elevation16,
9739
+ border: `${_pxToRem(2)} solid ${theme.palette.neutralPrimary}`
9740
+ }
9741
+ }
9742
+ }, localVideoModalStyles);
9743
+ };
9744
+ /**
9745
+ * Padding equal to the amount the modal should stay inside the bounds of the container.
9746
+ * i.e. if this is 8px, the modal should always be at least 8px inside the container at all times on all sides.
9747
+ * @private
9748
+ */
9749
+ const localVideoTileOuterPaddingPX = 8;
9750
+ /**
9751
+ * @private
9752
+ */
9753
+ const floatingLocalVideoTileStyle = {
9754
+ root: {
9755
+ position: 'absolute',
9756
+ zIndex: LOCAL_VIDEO_TILE_ZINDEX,
9757
+ height: '100%',
9758
+ width: '100%'
9759
+ }
9542
9760
  };
9543
9761
  /**
9544
- * Hook to determine which participants should be in grid and horizontal gallery and their order respectively
9762
+ * Styles for the local video tile modal when it is focused, will cause keyboard move icon to appear over video
9545
9763
  * @private
9546
9764
  */
9547
- const useOrganizedParticipants = (args) => {
9548
- /* @conditional-compile-remove(pinned-participants) */
9549
- return _useOrganizedParticipantsWithPinnedParticipants(args);
9765
+ const localVideoModalStyles = {
9766
+ keyboardMoveIconContainer: {
9767
+ zIndex: LOCAL_VIDEO_TILE_ZINDEX + 1 // zIndex to set the keyboard movement Icon above the other layers in the video tile.
9768
+ }
9550
9769
  };
9551
9770
 
9552
9771
  // Copyright (c) Microsoft Corporation.
9553
- // Licensed under the MIT license.
9554
9772
  /**
9555
- * Horizontal Gallery button width in rem
9773
+ * VerticalGallery tile size in rem:
9774
+ *
9775
+ * min - smallest possible size of the tile (90px)
9776
+ * max - Largest size we want the vertical tiles (144px)
9777
+ *
9778
+ * @private
9556
9779
  */
9557
- const HORIZONTAL_GALLERY_BUTTON_WIDTH = 1.75;
9780
+ const VERTICAL_GALLERY_TILE_SIZE_REM = { minHeight: 5.625, maxHeight: 9, width: 9 };
9558
9781
  /**
9559
- * @private
9782
+ * Styles for the VerticalGallery's container set in parent.
9783
+ *
9784
+ * width is being increased by 1rem to account for the gap width desired for the VerticalGallery.
9785
+ *
9786
+ * @param shouldFloatLocalVideo whether rendered in floating layout or not
9787
+ * @returns Style set for VerticalGallery container.
9560
9788
  */
9561
- const leftRightButtonStyles = (theme) => {
9789
+ const verticalGalleryContainerStyle = (shouldFloatLocalVideo) => {
9562
9790
  return {
9563
- background: 'none',
9564
- padding: 0,
9565
- height: 'auto',
9566
- minWidth: `${HORIZONTAL_GALLERY_BUTTON_WIDTH}rem`,
9567
- maxWidth: `${HORIZONTAL_GALLERY_BUTTON_WIDTH}rem`,
9568
- border: `1px solid ${theme.palette.neutralLight}`,
9569
- borderRadius: theme.effects.roundedCorner4
9791
+ width: `${VERTICAL_GALLERY_TILE_SIZE_REM.width}rem`,
9792
+ height: shouldFloatLocalVideo ? `calc(100% - ${_pxToRem(VERTICAL_GALLERY_FLOATING_MODAL_SIZE_PX.height)})` : '100%',
9793
+ paddingBottom: '0.5rem'
9570
9794
  };
9571
9795
  };
9572
- /**
9573
- * Horizontal Gallery gap size in rem between tiles and buttons
9574
- */
9575
- const HORIZONTAL_GALLERY_GAP = 0.5;
9576
9796
  /**
9577
9797
  * @private
9578
9798
  */
9579
- const rootStyle = {
9580
- height: '100%',
9581
- width: '100%',
9582
- gap: `${HORIZONTAL_GALLERY_GAP}rem`
9799
+ const VERTICAL_GALLERY_TILE_STYLE = {
9800
+ minHeight: `${VERTICAL_GALLERY_TILE_SIZE_REM.minHeight}rem`,
9801
+ minWidth: `${VERTICAL_GALLERY_TILE_SIZE_REM.width}rem`,
9802
+ maxHeight: `${VERTICAL_GALLERY_TILE_SIZE_REM.maxHeight}rem`,
9803
+ maxWidth: `${VERTICAL_GALLERY_TILE_SIZE_REM.width}rem`
9583
9804
  };
9584
9805
  /**
9585
9806
  * @private
9586
9807
  */
9587
- const childrenContainerStyle = {
9588
- height: '100%',
9589
- gap: `${HORIZONTAL_GALLERY_GAP}rem`
9808
+ const verticalGalleryStyle = {
9809
+ children: VERTICAL_GALLERY_TILE_STYLE
9590
9810
  };
9591
9811
 
9592
9812
  // Copyright (c) Microsoft Corporation.
9593
9813
  /**
9594
- * {@link HorizontalGallery} default children per page
9814
+ * Responsive container for the VerticalGallery Component. Performs calculations for number of children
9815
+ * for the VerticalGallery
9816
+ * @param props
9817
+ *
9818
+ * @beta
9595
9819
  */
9596
- const DEFAULT_CHILDREN_PER_PAGE = 5;
9820
+ const ResponsiveVerticalGallery = (props) => {
9821
+ const { children, containerStyles, verticalGalleryStyles, gapHeightRem, controlBarHeightRem } = props;
9822
+ const containerRef = React.useRef(null);
9823
+ const containerHeight = _useContainerHeight(containerRef);
9824
+ const topPadding = containerRef.current ? parseFloat(getComputedStyle(containerRef.current).paddingTop) : 0;
9825
+ const bottomPadding = containerRef.current ? parseFloat(getComputedStyle(containerRef.current).paddingBottom) : 0;
9826
+ const childrenPerPage = calculateChildrenPerPage({
9827
+ numberOfChildren: React__default['default'].Children.count(children),
9828
+ containerHeight: (containerHeight !== null && containerHeight !== void 0 ? containerHeight : 0) - topPadding - bottomPadding,
9829
+ gapHeightRem,
9830
+ controlBarHeight: controlBarHeightRem !== null && controlBarHeightRem !== void 0 ? controlBarHeightRem : 2
9831
+ });
9832
+ return (React__default['default'].createElement("div", { ref: containerRef, className: react.mergeStyles(containerStyles) },
9833
+ React__default['default'].createElement(VerticalGallery, { childrenPerPage: childrenPerPage, styles: verticalGalleryStyles }, children)));
9834
+ };
9597
9835
  /**
9598
- * Renders a horizontal gallery that parents children horizontally. Handles pagination based on the childrenPerPage prop.
9599
- * @param props - HorizontalGalleryProps {@link @azure/communication-react#HorizontalGalleryProps}
9600
- * @returns
9836
+ * Helper function to find the number of children for the VerticalGallery on each page.
9601
9837
  */
9602
- const HorizontalGallery = (props) => {
9603
- var _a, _b;
9604
- const { children, childrenPerPage = DEFAULT_CHILDREN_PER_PAGE, styles } = props;
9605
- const ids = useIdentifiers();
9606
- const [page, setPage] = React.useState(0);
9607
- const numberOfChildren = React__default['default'].Children.count(children);
9608
- const lastPage = Math.ceil(numberOfChildren / childrenPerPage) - 1;
9609
- const paginatedChildren = React.useMemo(() => {
9610
- return bucketize(React__default['default'].Children.toArray(children), childrenPerPage);
9611
- }, [children, childrenPerPage]);
9612
- // If children per page is 0 or less return empty element
9613
- if (childrenPerPage <= 0) {
9614
- return React__default['default'].createElement(React__default['default'].Fragment, null);
9838
+ const calculateChildrenPerPage = (args) => {
9839
+ const { numberOfChildren, containerHeight, gapHeightRem, controlBarHeight } = args;
9840
+ const childMinHeightPx = _convertRemToPx(VERTICAL_GALLERY_TILE_SIZE_REM.minHeight);
9841
+ const gapHeightPx = _convertRemToPx(gapHeightRem);
9842
+ const controlBarHeightPx = _convertRemToPx(controlBarHeight);
9843
+ /** First check how many children can fit in containerHeight.
9844
+ *
9845
+ * _________________
9846
+ * | |
9847
+ * | |
9848
+ * |________________|
9849
+ * _________________
9850
+ * | |
9851
+ * | |
9852
+ * |________________|
9853
+ *
9854
+ * < n/m >
9855
+ *
9856
+ * number of children = container height - (2* gap height + button height) / childMinHeight
9857
+ *
9858
+ * we want to find the maximum number of children at the smallest size we can fit in the gallery and then resize them
9859
+ * to fill in the space as much as possible
9860
+ *
9861
+ * First we will find the max number of children without any controls we can fit.
9862
+ */
9863
+ const maxNumberOfChildrenInContainer = Math.floor((containerHeight + gapHeightPx) / (childMinHeightPx + gapHeightPx));
9864
+ // if all of the children fit in the container just return the number of children
9865
+ if (numberOfChildren <= maxNumberOfChildrenInContainer) {
9866
+ return maxNumberOfChildrenInContainer;
9615
9867
  }
9616
- const firstIndexOfCurrentPage = page * childrenPerPage;
9617
- const clippedPage = firstIndexOfCurrentPage < numberOfChildren - 1 ? page : lastPage;
9618
- const childrenOnCurrentPage = paginatedChildren[clippedPage];
9619
- const showButtons = numberOfChildren > childrenPerPage;
9620
- const disablePreviousButton = page === 0;
9621
- const disableNextButton = page === lastPage;
9622
- return (React__default['default'].createElement(react.Stack, { horizontal: true, className: react.mergeStyles(rootStyle, (_a = props.styles) === null || _a === void 0 ? void 0 : _a.root) },
9623
- showButtons && (React__default['default'].createElement(HorizontalGalleryNavigationButton, { key: "previous-nav-button", icon: React__default['default'].createElement(react.Icon, { iconName: "HorizontalGalleryLeftButton" }), styles: styles === null || styles === void 0 ? void 0 : styles.previousButton, onClick: () => setPage(Math.max(0, Math.min(lastPage, page - 1))), disabled: disablePreviousButton, identifier: ids.horizontalGalleryLeftNavButton })),
9624
- React__default['default'].createElement(react.Stack, { horizontal: true, className: react.mergeStyles(childrenContainerStyle, { '> *': (_b = props.styles) === null || _b === void 0 ? void 0 : _b.children }) }, childrenOnCurrentPage),
9625
- showButtons && (React__default['default'].createElement(HorizontalGalleryNavigationButton, { key: "next-nav-button", icon: React__default['default'].createElement(react.Icon, { iconName: "HorizontalGalleryRightButton" }), styles: styles === null || styles === void 0 ? void 0 : styles.nextButton, onClick: () => setPage(Math.min(lastPage, page + 1)), disabled: disableNextButton, identifier: ids.horizontalGalleryRightNavButton }))));
9626
- };
9627
- const HorizontalGalleryNavigationButton = (props) => {
9628
- const theme = useTheme();
9629
- return (React__default['default'].createElement(react.DefaultButton, { className: react.mergeStyles(leftRightButtonStyles(theme), props.styles), onClick: props.onClick, disabled: props.disabled, "data-ui-id": props.identifier }, props.icon));
9868
+ /**
9869
+ * For the pagination we know the container height, the height of the button bar and the 2 times the gap
9870
+ * height, top tile and bottom tile above control bar. So the child space is calculated as:
9871
+ *
9872
+ * space = height - controlbar - (2 * gap)
9873
+ */
9874
+ const childSpace = containerHeight - controlBarHeightPx - 2 * gapHeightPx;
9875
+ /**
9876
+ * Now that we have the childrenSpace height we can figure out how many Children can fir in the childrenSpace.
9877
+ * childrenSpace = n * childHeightMin + (n - 1) * gapHeight. isolate n and take the floor.
9878
+ */
9879
+ return Math.floor((childSpace + gapHeightPx) / (childMinHeightPx + gapHeightPx));
9630
9880
  };
9631
- function bucketize(arr, bucketSize) {
9632
- const bucketArray = [];
9633
- if (bucketSize <= 0) {
9634
- return bucketArray;
9635
- }
9636
- for (let i = 0; i < arr.length; i += bucketSize) {
9637
- bucketArray.push(arr.slice(i, i + bucketSize));
9638
- }
9639
- return bucketArray;
9640
- }
9641
9881
 
9642
9882
  // Copyright (c) Microsoft Corporation.
9643
9883
  /**
9644
- * Wrapped HorizontalGallery that adjusts the number of items per page based on the
9645
- * available width obtained from a ResizeObserver, width per child, gap width, and button width
9884
+ * @private
9646
9885
  */
9647
- const ResponsiveHorizontalGallery = (props) => {
9648
- const { childWidthRem, gapWidthRem, buttonWidthRem = 0 } = props;
9649
- const containerRef = React.useRef(null);
9650
- const containerWidth = _useContainerWidth(containerRef);
9651
- const leftPadding = containerRef.current ? parseFloat(getComputedStyle(containerRef.current).paddingLeft) : 0;
9652
- const rightPadding = containerRef.current ? parseFloat(getComputedStyle(containerRef.current).paddingRight) : 0;
9653
- const childrenPerPage = calculateChildrenPerPage({
9654
- numberOfChildren: React__default['default'].Children.count(props.children),
9655
- containerWidth: (containerWidth !== null && containerWidth !== void 0 ? containerWidth : 0) - leftPadding - rightPadding,
9656
- childWidthRem,
9657
- gapWidthRem,
9658
- buttonWidthRem
9659
- });
9660
- return (React__default['default'].createElement("div", { ref: containerRef, className: react.mergeStyles(props.containerStyles) },
9661
- React__default['default'].createElement(HorizontalGallery, { childrenPerPage: childrenPerPage, styles: props.horizontalGalleryStyles }, props.children)));
9886
+ const horizontalGalleryContainerStyle = (shouldFloatLocalVideo, isNarrow) => {
9887
+ return {
9888
+ minHeight: isNarrow
9889
+ ? `${SMALL_HORIZONTAL_GALLERY_TILE_SIZE_REM.height}rem`
9890
+ : `${LARGE_HORIZONTAL_GALLERY_TILE_SIZE_REM.height}rem`,
9891
+ width: shouldFloatLocalVideo
9892
+ ? isNarrow
9893
+ ? `calc(100% - ${_pxToRem(SMALL_FLOATING_MODAL_SIZE_PX.width)})`
9894
+ : `calc(100% - ${_pxToRem(LARGE_FLOATING_MODAL_SIZE_PX.width)})`
9895
+ : '100%',
9896
+ paddingRight: '0.5rem'
9897
+ };
9662
9898
  };
9663
9899
  /**
9664
- * Helper function to calculate children per page for HorizontalGallery based on width of container, child, buttons, and
9665
- * gaps in between
9900
+ * @private
9666
9901
  */
9667
- const calculateChildrenPerPage = (args) => {
9668
- const { numberOfChildren, containerWidth, buttonWidthRem, childWidthRem, gapWidthRem } = args;
9669
- const childWidth = _convertRemToPx(childWidthRem);
9670
- const gapWidth = _convertRemToPx(gapWidthRem);
9671
- /** First check how many children can fit in containerWidth.
9672
- * __________________________________
9673
- * | || |
9674
- * | || |
9675
- * |________________||________________|
9676
- * <-----------containerWidth--------->
9677
- * containerWidth = n * childWidth + (n - 1) * gapWidth. Isolate n and take the floor.
9678
- */
9679
- const numberOfChildrenInContainer = Math.floor((containerWidth + gapWidth) / (childWidth + gapWidth));
9680
- // If all children fit then return numberOfChildrenInContainer
9681
- if (numberOfChildren <= numberOfChildrenInContainer) {
9682
- return numberOfChildrenInContainer;
9902
+ const horizontalGalleryStyle = (isNarrow) => {
9903
+ return {
9904
+ children: isNarrow ? SMALL_HORIZONTAL_GALLERY_TILE_STYLE : LARGE_HORIZONTAL_GALLERY_TILE_STYLE
9905
+ };
9906
+ };
9907
+ /**
9908
+ * Small horizontal gallery tile size in rem
9909
+ * @private
9910
+ */
9911
+ const SMALL_HORIZONTAL_GALLERY_TILE_SIZE_REM = { height: 6.5, width: 6.5 };
9912
+ /**
9913
+ * Large horizontal gallery tile size in rem
9914
+ * @private
9915
+ */
9916
+ const LARGE_HORIZONTAL_GALLERY_TILE_SIZE_REM = { height: 7.5, width: 10 };
9917
+ /**
9918
+ * @private
9919
+ */
9920
+ const SMALL_HORIZONTAL_GALLERY_TILE_STYLE = {
9921
+ minHeight: `${SMALL_HORIZONTAL_GALLERY_TILE_SIZE_REM.height}rem`,
9922
+ minWidth: `${SMALL_HORIZONTAL_GALLERY_TILE_SIZE_REM.width}rem`,
9923
+ maxHeight: `${SMALL_HORIZONTAL_GALLERY_TILE_SIZE_REM.height}rem`,
9924
+ maxWidth: `${SMALL_HORIZONTAL_GALLERY_TILE_SIZE_REM.width}rem`
9925
+ };
9926
+ /**
9927
+ * @private
9928
+ */
9929
+ const LARGE_HORIZONTAL_GALLERY_TILE_STYLE = {
9930
+ minHeight: `${LARGE_HORIZONTAL_GALLERY_TILE_SIZE_REM.height}rem`,
9931
+ minWidth: `${LARGE_HORIZONTAL_GALLERY_TILE_SIZE_REM.width}rem`,
9932
+ maxHeight: `${LARGE_HORIZONTAL_GALLERY_TILE_SIZE_REM.height}rem`,
9933
+ maxWidth: `${LARGE_HORIZONTAL_GALLERY_TILE_SIZE_REM.width}rem`
9934
+ };
9935
+
9936
+ // Copyright (c) Microsoft Corporation.
9937
+ /**
9938
+ * @private
9939
+ */
9940
+ const scrollableHorizontalGalleryStyles = {
9941
+ root: {
9942
+ width: '100%',
9943
+ minHeight: `${SMALL_HORIZONTAL_GALLERY_TILE_SIZE_REM.height}rem`,
9944
+ paddingRight: '0.5rem',
9945
+ '> *': SMALL_HORIZONTAL_GALLERY_TILE_STYLE
9683
9946
  }
9684
- const buttonWidth = _convertRemToPx(buttonWidthRem);
9685
- /** We know we need to paginate. So we need to subtract the buttonWidth twice and gapWidth twice from
9686
- * containerWidth to compute childrenSpace
9687
- * <-----------containerWidth--------->
9688
- * __________________________________
9689
- * | || || || |
9690
- * |<|| || ||>|
9691
- * |_||_____________||_____________||_|
9692
- * <-------childrenSpace------>
9693
- */
9694
- const childrenSpace = containerWidth - 2 * buttonWidth - 2 * gapWidth;
9695
- // Now that we have childrenSpace width we can figure out how many children can fit in childrenSpace.
9696
- // childrenSpace = n * childWidth + (n - 1) * gapWidth. Isolate n and take the floor.
9697
- return Math.floor((childrenSpace + gapWidth) / (childWidth + gapWidth));
9947
+ };
9948
+ /**
9949
+ * @private
9950
+ */
9951
+ const scrollableHorizontalGalleryContainerStyles = react.mergeStyles({
9952
+ display: 'flex',
9953
+ width: `calc(100% - ${SMALL_FLOATING_MODAL_SIZE_PX.width}px)`,
9954
+ minHeight: `${SMALL_HORIZONTAL_GALLERY_TILE_SIZE_REM.height}rem`,
9955
+ overflow: 'scroll',
9956
+ '-ms-overflow-style': 'none',
9957
+ 'scrollbar-width': 'none',
9958
+ '::-webkit-scrollbar': { display: 'none' }
9959
+ });
9960
+
9961
+ // Copyright (c) Microsoft Corporation.
9962
+ /**
9963
+ * Component to display elements horizontally in a scrollable container
9964
+ * @private
9965
+ */
9966
+ const ScrollableHorizontalGallery = (props) => {
9967
+ const { horizontalGalleryElements } = props;
9968
+ const ref = React.useRef();
9969
+ const { events: dragabbleEvents } = reactUseDraggableScroll.useDraggable(ref);
9970
+ return (React__default['default'].createElement("div", Object.assign({ ref: ref }, dragabbleEvents, { className: scrollableHorizontalGalleryContainerStyles }),
9971
+ React__default['default'].createElement(react.Stack, { "data-ui-id": "scrollable-horizontal-gallery", horizontal: true, styles: scrollableHorizontalGalleryStyles, tokens: { childrenGap: '0.5rem' } }, horizontalGalleryElements)));
9698
9972
  };
9699
9973
 
9700
9974
  // Copyright (c) Microsoft Corporation.
@@ -9703,11 +9977,32 @@ const calculateChildrenPerPage = (args) => {
9703
9977
  *
9704
9978
  * @private
9705
9979
  */
9706
- const VideoGalleryResponsiveHorizontalGallery = (props) => {
9707
- const { shouldFloatLocalVideo = false, isNarrow = false, horizontalGalleryElements, styles } = props;
9708
- const containerStyles = React.useMemo(() => horizontalGalleryContainerStyle(shouldFloatLocalVideo, isNarrow), [shouldFloatLocalVideo, isNarrow]);
9709
- const galleryStyles = React.useMemo(() => react.concatStyleSets(horizontalGalleryStyle(isNarrow), styles), [isNarrow, styles]);
9710
- return (React__default['default'].createElement(ResponsiveHorizontalGallery, { key: "responsive-horizontal-gallery", containerStyles: containerStyles, horizontalGalleryStyles: galleryStyles, childWidthRem: isNarrow ? SMALL_HORIZONTAL_GALLERY_TILE_SIZE_REM.width : LARGE_HORIZONTAL_GALLERY_TILE_SIZE_REM.width, buttonWidthRem: HORIZONTAL_GALLERY_BUTTON_WIDTH, gapWidthRem: HORIZONTAL_GALLERY_GAP }, horizontalGalleryElements));
9980
+ const OverflowGallery = (props) => {
9981
+ const { shouldFloatLocalVideo = false, isNarrow = false, overflowGalleryElements, styles,
9982
+ /* @conditional-compile-remove(vertical-gallery) */ overflowGalleryLayout = 'HorizontalBottom' } = props;
9983
+ const containerStyles = React.useMemo(() => {
9984
+ /* @conditional-compile-remove(vertical-gallery) */
9985
+ if (overflowGalleryLayout === 'VerticalRight') {
9986
+ return verticalGalleryContainerStyle(shouldFloatLocalVideo);
9987
+ }
9988
+ return horizontalGalleryContainerStyle(shouldFloatLocalVideo, isNarrow);
9989
+ }, [shouldFloatLocalVideo, isNarrow, /* @conditional-compile-remove(vertical-gallery) */ overflowGalleryLayout]);
9990
+ const galleryStyles = React.useMemo(() => {
9991
+ /* @conditional-compile-remove(vertical-gallery) */
9992
+ if (overflowGalleryLayout === 'VerticalRight') {
9993
+ return react.concatStyleSets(verticalGalleryStyle, styles);
9994
+ }
9995
+ return react.concatStyleSets(horizontalGalleryStyle(isNarrow), styles);
9996
+ }, [isNarrow, styles, /* @conditional-compile-remove(vertical-gallery) */ overflowGalleryLayout]);
9997
+ /* @conditional-compile-remove(vertical-gallery) */
9998
+ if (overflowGalleryLayout === 'VerticalRight') {
9999
+ return (React__default['default'].createElement(ResponsiveVerticalGallery, { key: "responsive-vertical-gallery", containerStyles: containerStyles, verticalGalleryStyles: galleryStyles, controlBarHeightRem: HORIZONTAL_GALLERY_BUTTON_WIDTH, gapHeightRem: HORIZONTAL_GALLERY_GAP }, overflowGalleryElements));
10000
+ }
10001
+ /* @conditional-compile-remove(pinned-participants) */
10002
+ if (isNarrow) {
10003
+ return React__default['default'].createElement(ScrollableHorizontalGallery, { horizontalGalleryElements: overflowGalleryElements });
10004
+ }
10005
+ return (React__default['default'].createElement(ResponsiveHorizontalGallery, { key: "responsive-horizontal-gallery", containerStyles: containerStyles, horizontalGalleryStyles: galleryStyles, childWidthRem: isNarrow ? SMALL_HORIZONTAL_GALLERY_TILE_SIZE_REM.width : LARGE_HORIZONTAL_GALLERY_TILE_SIZE_REM.width, buttonWidthRem: HORIZONTAL_GALLERY_BUTTON_WIDTH, gapWidthRem: HORIZONTAL_GALLERY_GAP }, overflowGalleryElements));
9711
10006
  };
9712
10007
 
9713
10008
  // Copyright (c) Microsoft Corporation.
@@ -9719,7 +10014,8 @@ const VideoGalleryResponsiveHorizontalGallery = (props) => {
9719
10014
  */
9720
10015
  const DefaultLayout = (props) => {
9721
10016
  const { remoteParticipants = [], dominantSpeakers, localVideoComponent, screenShareComponent, onRenderRemoteParticipant, styles, maxRemoteVideoStreams, parentWidth,
9722
- /* @conditional-compile-remove(pinned-participants) */ pinnedParticipantUserIds } = props;
10017
+ /* @conditional-compile-remove(pinned-participants) */ pinnedParticipantUserIds,
10018
+ /* @conditional-compile-remove(vertical-gallery) */ overflowGalleryLayout = 'HorizontalBottom' } = props;
9723
10019
  const isNarrow = parentWidth ? isNarrowWidth(parentWidth) : false;
9724
10020
  const { gridParticipants, horizontalGalleryParticipants } = useOrganizedParticipants({
9725
10021
  remoteParticipants,
@@ -9744,19 +10040,26 @@ const DefaultLayout = (props) => {
9744
10040
  if (localVideoComponent) {
9745
10041
  gridTiles.push(localVideoComponent);
9746
10042
  }
9747
- const horizontalGallery = React.useMemo(() => {
10043
+ const overflowGallery = React.useMemo(() => {
9748
10044
  if (horizontalGalleryTiles.length === 0) {
9749
10045
  return null;
9750
10046
  }
9751
- /* @conditional-compile-remove(pinned-participants) */
9752
- if (isNarrow) {
9753
- return React__default['default'].createElement(ScrollableHorizontalGallery, { horizontalGalleryElements: horizontalGalleryTiles });
9754
- }
9755
- return (React__default['default'].createElement(VideoGalleryResponsiveHorizontalGallery, { isNarrow: isNarrow, shouldFloatLocalVideo: true, horizontalGalleryElements: horizontalGalleryTiles, styles: styles === null || styles === void 0 ? void 0 : styles.horizontalGallery }));
9756
- }, [isNarrow, horizontalGalleryTiles, styles === null || styles === void 0 ? void 0 : styles.horizontalGallery]);
9757
- return (React__default['default'].createElement(react.Stack, { horizontal: false, styles: rootLayoutStyle$1, tokens: videoGalleryLayoutGap },
10047
+ return (React__default['default'].createElement(OverflowGallery, { isNarrow: isNarrow, shouldFloatLocalVideo: false, overflowGalleryElements: horizontalGalleryTiles, styles: styles === null || styles === void 0 ? void 0 : styles.horizontalGallery,
10048
+ /* @conditional-compile-remove(pinned-participants) */
10049
+ overflowGalleryLayout: overflowGalleryLayout }));
10050
+ }, [
10051
+ isNarrow,
10052
+ horizontalGalleryTiles,
10053
+ styles === null || styles === void 0 ? void 0 : styles.horizontalGallery,
10054
+ /* @conditional-compile-remove(vertical-gallery) */ overflowGalleryLayout
10055
+ ]);
10056
+ return (React__default['default'].createElement(react.Stack
10057
+ /* @conditional-compile-remove(vertical-gallery) */
10058
+ , {
10059
+ /* @conditional-compile-remove(vertical-gallery) */
10060
+ horizontal: overflowGalleryLayout === 'VerticalRight', styles: rootLayoutStyle$1, tokens: videoGalleryLayoutGap },
9758
10061
  screenShareComponent ? (screenShareComponent) : (React__default['default'].createElement(GridLayout, { key: "grid-layout", styles: styles === null || styles === void 0 ? void 0 : styles.gridLayout }, gridTiles)),
9759
- horizontalGallery));
10062
+ overflowGallery));
9760
10063
  };
9761
10064
 
9762
10065
  // Copyright (c) Microsoft Corporation.
@@ -10447,21 +10750,19 @@ const modalMaxDragPosition = { x: localVideoTileOuterPaddingPX, y: localVideoTil
10447
10750
  * @private
10448
10751
  */
10449
10752
  const FloatingLocalVideo = (props) => {
10450
- const { localVideoComponent, layerHostId, isNarrow, parentWidth, parentHeight } = props;
10753
+ const { localVideoComponent, layerHostId, localVideoSize, parentWidth, parentHeight } = props;
10451
10754
  const theme = useTheme();
10452
- const modalWidth = isNarrow ? SMALL_FLOATING_MODAL_SIZE_PX.width : LARGE_FLOATING_MODAL_SIZE_PX.width;
10453
- const modalHeight = isNarrow ? SMALL_FLOATING_MODAL_SIZE_PX.height : LARGE_FLOATING_MODAL_SIZE_PX.height;
10454
10755
  // The minimum drag position is the top left of the video gallery. i.e. the modal (PiP) should not be able
10455
10756
  // to be dragged offscreen and these are the top and left bounds of that calculation.
10456
10757
  const modalMinDragPosition = React.useMemo(() => parentWidth && parentHeight
10457
10758
  ? {
10458
10759
  // We use -parentWidth/Height because our modal is positioned to start in the bottom right,
10459
10760
  // hence (0,0) is the bottom right of the video gallery.
10460
- x: -parentWidth + modalWidth + localVideoTileOuterPaddingPX,
10461
- y: -parentHeight + modalHeight + localVideoTileOuterPaddingPX
10761
+ x: -parentWidth + localVideoSize.width + localVideoTileOuterPaddingPX,
10762
+ y: -parentHeight + localVideoSize.height + localVideoTileOuterPaddingPX
10462
10763
  }
10463
- : undefined, [parentHeight, parentWidth, modalHeight, modalWidth]);
10464
- const modalStyles = React.useMemo(() => floatingLocalVideoModalStyle(theme, isNarrow), [theme, isNarrow]);
10764
+ : undefined, [parentHeight, parentWidth, localVideoSize.width, localVideoSize.height]);
10765
+ const modalStyles = React.useMemo(() => floatingLocalVideoModalStyle(theme, localVideoSize), [theme, localVideoSize]);
10465
10766
  const layerProps = React.useMemo(() => ({ hostId: layerHostId }), [layerHostId]);
10466
10767
  return (React__default['default'].createElement(_ModalClone, { isOpen: true, isModeless: true, dragOptions: DRAG_OPTIONS$1, styles: modalStyles, layerProps: layerProps, maxDragPosition: modalMaxDragPosition, minDragPosition: modalMinDragPosition }, localVideoComponent));
10467
10768
  };
@@ -10503,7 +10804,8 @@ const layerHostStyle = {
10503
10804
  */
10504
10805
  const FloatingLocalVideoLayout = (props) => {
10505
10806
  const { remoteParticipants = [], dominantSpeakers, localVideoComponent, screenShareComponent, onRenderRemoteParticipant, styles, maxRemoteVideoStreams, showCameraSwitcherInLocalPreview, parentWidth, parentHeight,
10506
- /* @conditional-compile-remove(pinned-participants) */ pinnedParticipantUserIds } = props;
10807
+ /* @conditional-compile-remove(pinned-participants) */ pinnedParticipantUserIds,
10808
+ /* @conditional-compile-remove(vertical-gallery) */ overflowGalleryLayout = 'HorizontalBottom' } = props;
10507
10809
  const theme = useTheme();
10508
10810
  const isNarrow = parentWidth ? isNarrowWidth(parentWidth) : false;
10509
10811
  const { gridParticipants, horizontalGalleryParticipants } = useOrganizedParticipants({
@@ -10531,28 +10833,43 @@ const FloatingLocalVideoLayout = (props) => {
10531
10833
  : (_b = p.videoStream) === null || _b === void 0 ? void 0 : _b.isAvailable);
10532
10834
  });
10533
10835
  const layerHostId = reactHooks.useId('layerhost');
10836
+ let localVideoSize = LARGE_FLOATING_MODAL_SIZE_PX;
10837
+ if (isNarrow) {
10838
+ localVideoSize = SMALL_FLOATING_MODAL_SIZE_PX;
10839
+ }
10840
+ /* @conditional-compile-remove(vertical-gallery) */
10841
+ if (overflowGalleryLayout === 'VerticalRight') {
10842
+ localVideoSize = VERTICAL_GALLERY_FLOATING_MODAL_SIZE_PX;
10843
+ }
10534
10844
  const wrappedLocalVideoComponent = localVideoComponent && shouldFloatLocalVideo ? (
10535
10845
  // When we use showCameraSwitcherInLocalPreview it disables dragging to allow keyboard navigation.
10536
- showCameraSwitcherInLocalPreview ? (React__default['default'].createElement(react.Stack, { className: react.mergeStyles(localVideoTileWithControlsContainerStyle(theme, isNarrow), {
10846
+ showCameraSwitcherInLocalPreview ? (React__default['default'].createElement(react.Stack, { className: react.mergeStyles(localVideoTileWithControlsContainerStyle(theme, localVideoSize), {
10537
10847
  boxShadow: theme.effects.elevation8,
10538
10848
  zIndex: LOCAL_VIDEO_TILE_ZINDEX
10539
- }) }, localVideoComponent)) : horizontalGalleryTiles.length > 0 ? (React__default['default'].createElement(react.Stack, { className: react.mergeStyles(localVideoTileContainerStyle(theme, isNarrow)) }, localVideoComponent)) : (React__default['default'].createElement(FloatingLocalVideo, { localVideoComponent: localVideoComponent, layerHostId: layerHostId, isNarrow: isNarrow, parentWidth: parentWidth, parentHeight: parentHeight }))) : undefined;
10540
- const horizontalGallery = React.useMemo(() => {
10849
+ }) }, localVideoComponent)) : horizontalGalleryTiles.length > 0 ? (React__default['default'].createElement(react.Stack, { className: react.mergeStyles(localVideoTileContainerStyle(theme, localVideoSize)) }, localVideoComponent)) : (React__default['default'].createElement(FloatingLocalVideo, { localVideoComponent: localVideoComponent, layerHostId: layerHostId, localVideoSize: localVideoSize, parentWidth: parentWidth, parentHeight: parentHeight }))) : undefined;
10850
+ const overflowGallery = React.useMemo(() => {
10541
10851
  if (horizontalGalleryTiles.length === 0) {
10542
10852
  return null;
10543
10853
  }
10544
- /* @conditional-compile-remove(pinned-participants) */
10545
- if (isNarrow) {
10546
- return React__default['default'].createElement(ScrollableHorizontalGallery, { horizontalGalleryElements: horizontalGalleryTiles });
10547
- }
10548
- return (React__default['default'].createElement(VideoGalleryResponsiveHorizontalGallery, { isNarrow: isNarrow, shouldFloatLocalVideo: true, horizontalGalleryElements: horizontalGalleryTiles, styles: styles === null || styles === void 0 ? void 0 : styles.horizontalGallery }));
10549
- }, [isNarrow, horizontalGalleryTiles, styles === null || styles === void 0 ? void 0 : styles.horizontalGallery]);
10854
+ return (React__default['default'].createElement(OverflowGallery, { isNarrow: isNarrow, shouldFloatLocalVideo: true, overflowGalleryElements: horizontalGalleryTiles, styles: styles === null || styles === void 0 ? void 0 : styles.horizontalGallery,
10855
+ /* @conditional-compile-remove(vertical-gallery) */
10856
+ overflowGalleryLayout: overflowGalleryLayout }));
10857
+ }, [
10858
+ isNarrow,
10859
+ horizontalGalleryTiles,
10860
+ styles === null || styles === void 0 ? void 0 : styles.horizontalGallery,
10861
+ /* @conditional-compile-remove(vertical-gallery) */ overflowGalleryLayout
10862
+ ]);
10550
10863
  return (React__default['default'].createElement(react.Stack, { styles: rootLayoutStyle },
10551
10864
  wrappedLocalVideoComponent,
10552
10865
  React__default['default'].createElement(react.LayerHost, { id: layerHostId, className: react.mergeStyles(layerHostStyle) }),
10553
- React__default['default'].createElement(react.Stack, { horizontal: false, styles: innerLayoutStyle, tokens: videoGalleryLayoutGap },
10866
+ React__default['default'].createElement(react.Stack
10867
+ /* @conditional-compile-remove(vertical-gallery) */
10868
+ , {
10869
+ /* @conditional-compile-remove(vertical-gallery) */
10870
+ horizontal: overflowGalleryLayout === 'VerticalRight', styles: innerLayoutStyle, tokens: videoGalleryLayoutGap },
10554
10871
  screenShareComponent ? (screenShareComponent) : (React__default['default'].createElement(GridLayout, { key: "grid-layout", styles: styles === null || styles === void 0 ? void 0 : styles.gridLayout }, gridTiles)),
10555
- horizontalGallery)));
10872
+ overflowGallery)));
10556
10873
  };
10557
10874
 
10558
10875
  // Copyright (c) Microsoft Corporation.
@@ -10588,7 +10905,9 @@ const VideoGallery = (props) => {
10588
10905
  /* @conditional-compile-remove(pinned-participants) */
10589
10906
  onUnpinParticipant: onUnpinParticipantHandler,
10590
10907
  /* @conditional-compile-remove(pinned-participants) */
10591
- remoteVideoTileMenuOptions = DEFAULT_REMOTE_VIDEO_TILE_MENU_OPTIONS } = props;
10908
+ remoteVideoTileMenuOptions = DEFAULT_REMOTE_VIDEO_TILE_MENU_OPTIONS,
10909
+ /* @conditional-compile-remove(vertical-gallery) */
10910
+ overflowGalleryLayout = 'HorizontalBottom' } = props;
10592
10911
  const ids = useIdentifiers();
10593
10912
  const theme = useTheme();
10594
10913
  const localeStrings = useLocale$1().strings.videoGallery;
@@ -10748,7 +11067,8 @@ const VideoGallery = (props) => {
10748
11067
  localVideoComponent: localVideoTile,
10749
11068
  parentWidth: containerWidth,
10750
11069
  parentHeight: containerHeight,
10751
- /* @conditional-compile-remove(pinned-participants) */ pinnedParticipantUserIds: pinnedParticipants
11070
+ /* @conditional-compile-remove(pinned-participants) */ pinnedParticipantUserIds: pinnedParticipants,
11071
+ /* @conditional-compile-remove(vertical-gallery) */ overflowGalleryLayout
10752
11072
  }), [
10753
11073
  remoteParticipants,
10754
11074
  screenShareComponent,
@@ -10761,7 +11081,8 @@ const VideoGallery = (props) => {
10761
11081
  containerHeight,
10762
11082
  onRenderRemoteVideoTile,
10763
11083
  defaultOnRenderVideoTile,
10764
- /* @conditional-compile-remove(pinned-participants) */ pinnedParticipants
11084
+ /* @conditional-compile-remove(pinned-participants) */ pinnedParticipants,
11085
+ /* @conditional-compile-remove(vertical-gallery) */ overflowGalleryLayout
10765
11086
  ]);
10766
11087
  const videoGalleryLayout = React.useMemo(() => {
10767
11088
  if (layout === 'floatingLocalVideo') {
@@ -14439,7 +14760,8 @@ class EventSubscriber {
14439
14760
  });
14440
14761
  this.fetchLastParticipantMessage(event.threadId, 'participantAdded');
14441
14762
  };
14442
- // This is a hot fix that no participant message is received for onChatMessageReceived event, which should be handled by JS SDK
14763
+ // This is a temporary fix that no participant message is received for onChatMessageReceived event, which should be handled by JS SDK.
14764
+ // Without the temporary fix, there are missing 'participant joined' and 'participant left' system messages in the chat thread.
14443
14765
  this.fetchLastParticipantMessage = (threadId, actionType) => __awaiter$i(this, void 0, void 0, function* () {
14444
14766
  var e_1, _a;
14445
14767
  try {
@@ -14465,7 +14787,13 @@ class EventSubscriber {
14465
14787
  return participant.id;
14466
14788
  });
14467
14789
  this.chatContext.deleteParticipants(event.threadId, participantIds);
14468
- this.fetchLastParticipantMessage(event.threadId, 'participantRemoved');
14790
+ // If the current user is removed from the thread, do not fetch the last participant message
14791
+ // as they no longer have access to the thread.
14792
+ const currentUserId = toFlatCommunicationIdentifier(this.chatContext.getState().userId);
14793
+ const wasCurrentUserRemoved = participantIds.find((id) => toFlatCommunicationIdentifier(id) === currentUserId);
14794
+ if (!wasCurrentUserRemoved) {
14795
+ this.fetchLastParticipantMessage(event.threadId, 'participantRemoved');
14796
+ }
14469
14797
  };
14470
14798
  this.onReadReceiptReceived = (event) => {
14471
14799
  const readReceipt = Object.assign(Object.assign({}, event), { sender: event.sender, readOn: new Date(event.readOn) });