Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
5f6abb5
add movement to the overflow gallery
dmceachernmsft Aug 1, 2023
0a24cca
move the local tile fix vertical
dmceachernmsft Aug 3, 2023
6b11936
fix sample build
dmceachernmsft Aug 3, 2023
f27f9e8
Change files
dmceachernmsft Aug 3, 2023
997ea63
Duplicate change files for beta release
dmceachernmsft Aug 3, 2023
784b328
Merge branch 'main' of https://github.com/Azure/communication-ui-libr…
dmceachernmsft Aug 3, 2023
a5a4edf
CC component level
dmceachernmsft Aug 8, 2023
c080b33
update composite controls
dmceachernmsft Aug 8, 2023
77e6c0a
Merge branch 'main' into dmceachernmsft-custom-layout-playground
dmceachernmsft Aug 8, 2023
d2b7308
add localization and icons
dmceachernmsft Aug 8, 2023
15ab947
Merge branch 'dmceachernmsft-custom-layout-playground' of https://git…
dmceachernmsft Aug 8, 2023
6452495
Beta API updates
dmceachernmsft Aug 8, 2023
7d50252
fix cc
dmceachernmsft Aug 8, 2023
add3c73
fix lint
dmceachernmsft Aug 8, 2023
10cd53e
Merge branch 'main' into dmceachernmsft-custom-layout-playground
dmceachernmsft Aug 8, 2023
d036d0a
Update packages/react-composites CallWithChatComposite browser test s…
github-actions[bot] Aug 8, 2023
442f4ef
Update packages/react-composites CallComposite browser test snapshots
github-actions[bot] Aug 8, 2023
760b269
Merge branch 'dmceachernmsft-custom-layout-playground' of https://git…
github-actions[bot] Aug 8, 2023
ae1a191
Merge branch 'main' into dmceachernmsft-custom-layout-playground
dmceachernmsft Aug 9, 2023
0003160
Merge branch 'main' into dmceachernmsft-custom-layout-playground
dmceachernmsft Aug 9, 2023
1b48608
create test for validating gallery movement
dmceachernmsft Aug 9, 2023
c4ddcbc
remove test only
dmceachernmsft Aug 9, 2023
5a9dbbc
Change files
dmceachernmsft Aug 9, 2023
1b77368
Duplicate change files for beta release
dmceachernmsft Aug 9, 2023
77dc341
Merge branch 'main' into dmceachernmsft-custom-layout-playground
dmceachernmsft Aug 9, 2023
c3bb1f9
update more button styles
dmceachernmsft Aug 10, 2023
619a1f4
create component
dmceachernmsft Aug 10, 2023
d4a9d69
update logic to just show speaker
dmceachernmsft Aug 10, 2023
bcbb268
maybe?
dmceachernmsft Aug 11, 2023
f7285d0
Merge branch 'main' of https://github.com/Azure/communication-ui-libr…
dmceachernmsft Aug 11, 2023
196c3ec
Merge branch 'main' into dmceachernmsft/overflow-movable-e2e
dmceachernmsft Aug 11, 2023
3efe86c
Update packages/react-composites CallWithChatComposite browser test s…
github-actions[bot] Aug 11, 2023
215e7ca
Merge branch 'main' into dmceachernmsft/overflow-movable-e2e
dmceachernmsft Aug 11, 2023
d3cb880
cc test
dmceachernmsft Aug 11, 2023
63d9758
Merge branch 'main' into dmceachernmsft/overflow-movable-e2e
dmceachernmsft Aug 11, 2023
4baacae
fix local tile
dmceachernmsft Aug 12, 2023
10cbd3e
Merge branch 'main' into dmceachernmsft/overflow-movable-e2e
dmceachernmsft Aug 14, 2023
e5bbd8e
Merge branch 'dmceachernmsft/overflow-movable-e2e' of https://github.…
dmceachernmsft Aug 14, 2023
7830cbd
Merge branch 'dmceachernmsft/overflow-movable-e2e' of https://github.…
dmceachernmsft Aug 14, 2023
459e52f
Change files
dmceachernmsft Aug 14, 2023
d17060b
Duplicate change files for beta release
dmceachernmsft Aug 14, 2023
ab5ff28
return composite to default
dmceachernmsft Aug 14, 2023
e3ab8b7
fix issues when alone in call with speaker layout
dmceachernmsft Aug 14, 2023
a2161e3
build API
dmceachernmsft Aug 14, 2023
cf764d1
fix build
dmceachernmsft Aug 14, 2023
eff341b
Update packages/react-composites CallComposite browser test snapshots
github-actions[bot] Aug 14, 2023
60043ba
update API
dmceachernmsft Aug 14, 2023
4473886
fix tests
dmceachernmsft Aug 14, 2023
1bf755f
fix icon positioning
dmceachernmsft Aug 14, 2023
4e0414a
Merge branch 'main' into dmceachernmsft/overflow-movable-e2e
dmceachernmsft Aug 14, 2023
9a18c3a
Merge branch 'dmceachernmsft/overflow-movable-e2e' of https://github.…
dmceachernmsft Aug 14, 2023
e6fc24a
remove not implemented API
dmceachernmsft Aug 14, 2023
bb33322
Update packages/react-composites CallWithChatComposite browser test s…
github-actions[bot] Aug 14, 2023
1596dcb
Update packages/react-composites CallComposite browser test snapshots
github-actions[bot] Aug 14, 2023
2844160
Merge branch 'dmceachernmsft/overflow-movable-e2e' of https://github.…
github-actions[bot] Aug 14, 2023
88bb358
Merge branch 'main' into dmceachernmsft/overflow-movable-e2e
dmceachernmsft Aug 15, 2023
75449b4
Merge branch 'dmceachernmsft/overflow-movable-e2e' of https://github.…
dmceachernmsft Aug 15, 2023
4dc2001
Merge branch 'main' into dmceachernmsft/speaker-gallery-component
dmceachernmsft Aug 15, 2023
2e08c73
Merge branch 'main' into dmceachernmsft/speaker-gallery-component
dmceachernmsft Aug 15, 2023
6bd9148
Merge branch 'main' into dmceachernmsft/speaker-gallery-component
dmceachernmsft Aug 15, 2023
61e5260
Merge branch 'main' into dmceachernmsft/speaker-gallery-component
dmceachernmsft Aug 16, 2023
3d45f29
Merge branch 'main' into dmceachernmsft/speaker-gallery-component
dmceachernmsft Aug 16, 2023
f66194b
Merge branch 'main' into dmceachernmsft/speaker-gallery-component
dmceachernmsft Aug 16, 2023
ae78372
Merge branch 'main' into dmceachernmsft/speaker-gallery-component
dmceachernmsft Aug 17, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"type": "prerelease",
"area": "feature",
"workstream": "Custom Layouts",
"comment": "Introduces Speaker gallery layout to the video gallery component",
"packageName": "@azure/communication-react",
"email": "94866715+dmceachernmsft@users.noreply.github.com",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"type": "prerelease",
"area": "feature",
"workstream": "Custom Layouts",
"comment": "Introduces Speaker gallery layout to the video gallery component",
"packageName": "@azure/communication-react",
"email": "94866715+dmceachernmsft@users.noreply.github.com",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
Expand Up @@ -3850,7 +3850,7 @@ export interface VideoBackgroundReplacementEffect extends BackgroundReplacementC
export const VideoGallery: (props: VideoGalleryProps) => JSX.Element;

// @public (undocumented)
export type VideoGalleryLayout = 'default' | 'floatingLocalVideo';
export type VideoGalleryLayout = 'default' | 'floatingLocalVideo' | /* @conditional-compile-remove(gallery-layouts) */ 'speaker';

// @public
export interface VideoGalleryLocalParticipant extends VideoGalleryParticipant {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2134,7 +2134,7 @@ export interface _VideoEffectsItemStyles {
export const VideoGallery: (props: VideoGalleryProps) => JSX.Element;

// @public (undocumented)
export type VideoGalleryLayout = 'default' | 'floatingLocalVideo';
export type VideoGalleryLayout = 'default' | 'floatingLocalVideo' | /* @conditional-compile-remove(gallery-layouts) */ 'speaker';

// @public
export interface VideoGalleryLocalParticipant extends VideoGalleryParticipant {
Expand Down
22 changes: 17 additions & 5 deletions packages/react-components/src/components/VideoGallery.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ import { floatingLocalVideoTileStyle } from './VideoGallery/styles/FloatingLocal
import { useId } from '@fluentui/react-hooks';
/* @conditional-compile-remove(vertical-gallery) */
import { VerticalGalleryStyles } from './VerticalGallery';
/* @conditional-compile-remove(gallery-layouts) */
import { SpeakerVideoLayout } from './VideoGallery/SpeakerVideoLayout';

/**
* @private
Expand Down Expand Up @@ -123,7 +125,10 @@ export interface VideoGalleryStrings {
/**
* @public
*/
export type VideoGalleryLayout = 'default' | 'floatingLocalVideo';
export type VideoGalleryLayout =
| 'default'
| 'floatingLocalVideo'
| /* @conditional-compile-remove(gallery-layouts) */ 'speaker';

/**
* {@link VideoGallery} Component Styles.
Expand Down Expand Up @@ -353,7 +358,10 @@ export const VideoGallery = (props: VideoGalleryProps): JSX.Element => {
/* @conditional-compile-remove(pinned-participants) */
const drawerMenuHostId = useId('drawerMenuHost', drawerMenuHostIdFromProp);

const shouldFloatLocalVideo = !!(layout === 'floatingLocalVideo' && remoteParticipants.length > 0);
const localTileNotInGrid = !!(
(layout === 'floatingLocalVideo' || /* @conditional-compile-remove(gallery-layouts) */ layout === 'speaker') &&
remoteParticipants.length > 0
);

const containerRef = useRef<HTMLDivElement>(null);
const containerWidth = _useContainerWidth(containerRef);
Expand Down Expand Up @@ -388,7 +396,7 @@ export const VideoGallery = (props: VideoGalleryProps): JSX.Element => {
}

const localVideoTileStyles = concatStyleSets(
shouldFloatLocalVideo ? floatingLocalVideoTileStyle : {},
localTileNotInGrid ? floatingLocalVideoTileStyle : {},
{
root: { borderRadius: theme.effects.roundedCorner4 }
},
Expand Down Expand Up @@ -418,7 +426,7 @@ export const VideoGallery = (props: VideoGalleryProps): JSX.Element => {
onRenderAvatar={onRenderAvatar}
showLabel={
!(
(shouldFloatLocalVideo && isNarrow) ||
(localTileNotInGrid && isNarrow) ||
/*@conditional-compile-remove(click-to-call) */ /* @conditional-compile-remove(rooms) */ localVideoTileSize ===
'9:16'
)
Expand All @@ -441,7 +449,7 @@ export const VideoGallery = (props: VideoGalleryProps): JSX.Element => {
onDisposeLocalStreamView,
onRenderAvatar,
onRenderLocalVideoTile,
shouldFloatLocalVideo,
localTileNotInGrid,
showCameraSwitcherInLocalPreview,
showMuteIndicator,
strings.localVideoCameraSwitcherLabel,
Expand Down Expand Up @@ -620,6 +628,10 @@ export const VideoGallery = (props: VideoGalleryProps): JSX.Element => {
if (layout === 'floatingLocalVideo') {
return <FloatingLocalVideoLayout {...layoutProps} />;
}
/* @conditional-compile-remove(gallery-layouts) */
if (layout === 'speaker') {
return <SpeakerVideoLayout {...layoutProps} />;
}
return <DefaultLayout {...layoutProps} />;
}, [layout, layoutProps]);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ export const DefaultLayout = (props: DefaultLayoutProps): JSX.Element => {
maxOverflowGalleryDominantSpeakers: screenShareComponent
? childrenPerPage.current - ((pinnedParticipantUserIds.length + 1) % childrenPerPage.current)
: childrenPerPage.current,
/* @conditional-compile-remove(pinned-participants) */ pinnedParticipantUserIds
/* @conditional-compile-remove(pinned-participants) */ pinnedParticipantUserIds,
/* @conditional-compile-remove(gallery-layouts) */ layout: 'default'
});

let activeVideoStreams = 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,8 @@ export const FloatingLocalVideoLayout = (props: FloatingLocalVideoLayoutProps):
maxOverflowGalleryDominantSpeakers: screenShareComponent
? childrenPerPage.current - (pinnedParticipantUserIds.length % childrenPerPage.current)
: childrenPerPage.current,
/* @conditional-compile-remove(pinned-participants) */ pinnedParticipantUserIds
/* @conditional-compile-remove(pinned-participants) */ pinnedParticipantUserIds,
/* @conditional-compile-remove(gallery-layouts) */ layout: 'floatingLocalVideo'
});

let activeVideoStreams = 0;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,249 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

import { LayerHost, Stack, mergeStyles, useTheme } from '@fluentui/react';
/* @conditional-compile-remove(click-to-call) */
import { LocalVideoTileSize } from '../VideoGallery';
import { LayoutProps } from './Layout';
import { isNarrowWidth } from '../utils/responsive';
/* @conditional-compile-remove(vertical-gallery) */
import { isShortHeight } from '../utils/responsive';
import React, { useMemo, useRef, useState } from 'react';
import { OverflowGallery } from './OverflowGallery';
import {
SMALL_FLOATING_MODAL_SIZE_REM,
LARGE_FLOATING_MODAL_SIZE_REM,
localVideoTileContainerStyle
} from './styles/FloatingLocalVideo.styles';
/* @conditional-compile-remove(vertical-gallery) */
import {
VERTICAL_GALLERY_FLOATING_MODAL_SIZE_REM,
SHORT_VERTICAL_GALLERY_FLOATING_MODAL_SIZE_REM
} from './styles/FloatingLocalVideo.styles';
import { useOrganizedParticipants } from './utils/videoGalleryLayoutUtils';
import { GridLayout } from '../GridLayout';
import { rootLayoutStyle } from './styles/FloatingLocalVideoLayout.styles';
import { layerHostStyle, innerLayoutStyle } from './styles/FloatingLocalVideoLayout.styles';
import { videoGalleryLayoutGap } from './styles/Layout.styles';
import { useId } from '@fluentui/react-hooks';

/**
* Props for {@link FloatingLocalVideoLayout}.
*
* @private
*/
export interface SpeakerVideoLayoutProps extends LayoutProps {
/**
* Whether to display the local video camera switcher button
*/
showCameraSwitcherInLocalPreview?: boolean;
/**
* Height of parent element
*/
parentHeight?: number;
/* @conditional-compile-remove(click-to-call) */
/**
* Local video tile mode
*/
localVideoTileSize?: LocalVideoTileSize;
}

/**
* Layout for the gallery mode to highlight the current dominant speaker
*
* @private
*/
export const SpeakerVideoLayout = (props: SpeakerVideoLayoutProps): JSX.Element => {
const {
remoteParticipants = [],
dominantSpeakers,
localVideoComponent,
screenShareComponent,
onRenderRemoteParticipant,
styles,
maxRemoteVideoStreams,
parentWidth,
/* @conditional-compile-remove(vertical-gallery) */ parentHeight,
/* @conditional-compile-remove(vertical-gallery) */ overflowGalleryPosition = 'HorizontalBottom',
pinnedParticipantUserIds = [],
/* @conditional-compile-remove(click-to-call) */ localVideoTileSize
} = props;

const theme = useTheme();

const isNarrow = parentWidth ? isNarrowWidth(parentWidth) : false;

/* @conditional-compile-remove(vertical-gallery) */
const isShort = parentHeight ? isShortHeight(parentHeight) : false;

// This is for tracking the number of children in the first page of overflow gallery.
// This number will be used for the maxOverflowGalleryDominantSpeakers when organizing the remote participants.
const childrenPerPage = useRef(4);
const { gridParticipants, overflowGalleryParticipants } = useOrganizedParticipants({
remoteParticipants,
dominantSpeakers,
maxRemoteVideoStreams,
isScreenShareActive: !!screenShareComponent,
maxOverflowGalleryDominantSpeakers: screenShareComponent
? childrenPerPage.current - (pinnedParticipantUserIds.length % childrenPerPage.current)
: childrenPerPage.current,
/* @conditional-compile-remove(pinned-participants) */ pinnedParticipantUserIds,
/* @conditional-compile-remove(gallery-layouts) */ layout: 'speaker'
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm totally fine with adding a layout prop to useOrganizedParticipants but I am curious if you can instead set maxRemoteVideoStreams as 0 and set remoteParticipants as remoteParticipants.filter(p => userId !== dominantSpeaker[0]) and gridTiles below (on line 97) would just be remoteParticipants.find(p => userId === dominantSpeaker[0]). Even if this works, it may be less intuitive for code readers

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That I think would work, but I like the use of the layout to do this determination, helps understand why we do or don't do something other than just the filtering of participants.

});

let activeVideoStreams = 0;

const gridTiles = gridParticipants.map((p) => {
return onRenderRemoteParticipant(
p,
maxRemoteVideoStreams && maxRemoteVideoStreams >= 0
? p.videoStream?.isAvailable && activeVideoStreams++ < maxRemoteVideoStreams
: p.videoStream?.isAvailable
);
});

const shouldFloatLocalVideo = remoteParticipants.length > 0;
Copy link
Copy Markdown
Member

@JamesBurnside JamesBurnside Aug 17, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Concerned there is so much duplicate logic between these different layouts (and we're adding more layout(s)). It increases the chance there's a bug with just one layout that doesn't appear in others and makes it really hard to add a feature to all layouts. (it also makes it very hard to review as there isn't a good way to compare this layout with other layouts without having the up side by side)

Not in this PR, but strongly consider refactoring how we structure these layouts. Ideally we split the business logic (of deciding what tile should be shown where) with the UI logic (the actual rendering of the tiles).
One possibility would be to:

const NewGalleryLayout = props => {
  const tileDefinitions = getTileDefinitions('newLayout', localParticipant, remoteParticipants, dominantSpeakers, height, width, etc.);
  // returns something like:
  // {
  //    primaryGridTiles: [remoteParticipantId, remoteParticipantId, remoteParticipantId],
  //    overflowTiles: [...],
  //    floatingTile: [],
  //    dockedCornerTile: [localParticipant]
  // }

  // then have memoized tile factories that return JSX elements that are the tiles:
  const primaryTiles = buildPrimaryTiles(tileDefinitions, renderOverrides, etc...);
  const overflowTiles = getOverflowTiles(tileDefinitions, renderOverrides, etc...);
  const floatingTile .. etc.etc.

  // Then have a single gallery with the places to put these:
  <VideoGalleryBase
    primaryTiles={primaryTiles}
    overflowTiles={overflowTiles}
    floatingTile={floatingTile}
    etc.etc.
  />
}

i.e. have

getTiles(constraints); // just state where this is business logic that is isolated and easily unit tested
buildTiles(tileDefinitions) // just constructing JSX where the tiles can be memoized and reused when layouts switch
<Gallery {...tiles} /> // just positions tiles it receives appropriately, completely layout agnostic

Just thinking of this while typing so not a perfect solution, but something where we can split UI and tile logic, then easily unit test the tile logic with differing constraints.

If we ever did go down this route we could utilize a component specific provider to store tiles such that they don't get recreated for different layouts as well where tiles get memoized and then if they're already built the buildXTiles method just grabs a memoized tile without having to construct a new one.


if (!shouldFloatLocalVideo && localVideoComponent) {
gridTiles.push(localVideoComponent);
}

/**
* instantiate indexes available to render with indexes available that would be on first page
*
* For some components which do not strictly follow the order of the array, we might
* re-render the initial tiles -> dispose them -> create new tiles, we need to take care of
* this case when those components are here
*/
const [indexesToRender, setIndexesToRender] = useState<number[]>([]);

const overflowGalleryTiles = overflowGalleryParticipants.map((p, i) => {
return onRenderRemoteParticipant(
p,
maxRemoteVideoStreams && maxRemoteVideoStreams >= 0
? p.videoStream?.isAvailable &&
indexesToRender &&
indexesToRender.includes(i) &&
activeVideoStreams++ < maxRemoteVideoStreams
: p.videoStream?.isAvailable
);
});

const layerHostId = useId('layerhost');

const localVideoSizeRem = useMemo(() => {
if (isNarrow || /*@conditional-compile-remove(click-to-call) */ localVideoTileSize === '9:16') {
return SMALL_FLOATING_MODAL_SIZE_REM;
}
/* @conditional-compile-remove(vertical-gallery) */
if ((overflowGalleryTiles.length > 0 || screenShareComponent) && overflowGalleryPosition === 'VerticalRight') {
return isNarrow
? SMALL_FLOATING_MODAL_SIZE_REM
: isShort
? SHORT_VERTICAL_GALLERY_FLOATING_MODAL_SIZE_REM
: VERTICAL_GALLERY_FLOATING_MODAL_SIZE_REM;
}
/*@conditional-compile-remove(click-to-call) */
if ((overflowGalleryTiles.length > 0 || screenShareComponent) && overflowGalleryPosition === 'HorizontalBottom') {
return localVideoTileSize === '16:9' || !isNarrow ? LARGE_FLOATING_MODAL_SIZE_REM : SMALL_FLOATING_MODAL_SIZE_REM;
}
return LARGE_FLOATING_MODAL_SIZE_REM;
}, [
overflowGalleryTiles.length,
isNarrow,
screenShareComponent,
/* @conditional-compile-remove(vertical-gallery) */ isShort,
/* @conditional-compile-remove(vertical-gallery) */ overflowGalleryPosition,
/* @conditional-compile-remove(click-to-call) */ localVideoTileSize
]);

const wrappedLocalVideoComponent =
localVideoComponent || (screenShareComponent && localVideoComponent) ? (
<Stack
className={mergeStyles(
localVideoTileContainerStyle(
theme,
localVideoSizeRem,
!!screenShareComponent,
/* @conditional-compile-remove(gallery-layouts) */ overflowGalleryPosition
)
)}
>
{localVideoComponent}
</Stack>
) : undefined;

const overflowGallery = useMemo(() => {
if (overflowGalleryTiles.length === 0 && !screenShareComponent) {
return null;
}
return (
<OverflowGallery
/* @conditional-compile-remove(vertical-gallery) */
isShort={isShort}
onFetchTilesToRender={setIndexesToRender}
isNarrow={isNarrow}
shouldFloatLocalVideo={true}
overflowGalleryElements={overflowGalleryTiles}
horizontalGalleryStyles={styles?.horizontalGallery}
/* @conditional-compile-remove(vertical-gallery) */
verticalGalleryStyles={styles?.verticalGallery}
/* @conditional-compile-remove(vertical-gallery) */
overflowGalleryPosition={overflowGalleryPosition}
onChildrenPerPageChange={(n: number) => {
childrenPerPage.current = n;
}}
/>
);
}, [
isNarrow,
/* @conditional-compile-remove(vertical-gallery) */ isShort,
screenShareComponent,
overflowGalleryTiles,
styles?.horizontalGallery,
/* @conditional-compile-remove(vertical-gallery) */ overflowGalleryPosition,
setIndexesToRender,
/* @conditional-compile-remove(vertical-gallery) */ styles?.verticalGallery
]);

return (
<Stack styles={rootLayoutStyle}>
{wrappedLocalVideoComponent}
<LayerHost id={layerHostId} className={mergeStyles(layerHostStyle)} />
<Stack
/* @conditional-compile-remove(vertical-gallery) */
horizontal={overflowGalleryPosition === 'VerticalRight'}
styles={innerLayoutStyle}
tokens={videoGalleryLayoutGap}
>
{
/* @conditional-compile-remove(gallery-layouts) */ props.overflowGalleryPosition === 'HorizontalTop' ? (
overflowGallery
) : (
<></>
)
}
{screenShareComponent ? (
screenShareComponent
) : (
<GridLayout key="grid-layout" styles={styles?.gridLayout}>
{gridTiles}
</GridLayout>
)}
{overflowGalleryTrampoline(
overflowGallery,
/* @conditional-compile-remove(gallery-layouts) */ props.overflowGalleryPosition
)}
</Stack>
</Stack>
);
};

const overflowGalleryTrampoline = (
gallery: JSX.Element | null,
galleryPosition?: 'HorizontalBottom' | 'VerticalRight' | 'HorizontalTop'
): JSX.Element | null => {
/* @conditional-compile-remove(gallery-layouts) */
return galleryPosition !== 'HorizontalTop' ? gallery : <></>;
return gallery;
};
Loading