UNPKG

communication-react-19

Version:

React library for building modern communication user experiences utilizing Azure Communication Services (React 19 compatible fork)

117 lines 8.02 kB
// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. import { LayerHost, mergeStyles, Stack } from '@fluentui/react'; import { useId } from '@fluentui/react-hooks'; import React, { useMemo, useRef, useState } from 'react'; import { useTheme } from '../../theming'; import { GridLayout } from '../GridLayout'; import { isNarrowWidth } from '../utils/responsive'; import { isShortHeight } from '../utils/responsive'; import { FloatingLocalVideo } from './FloatingLocalVideo'; import { LARGE_FLOATING_MODAL_SIZE_REM, localVideoTileContainerStyle, localVideoTileWithControlsContainerStyle, LOCAL_VIDEO_TILE_ZINDEX, SMALL_FLOATING_MODAL_SIZE_REM } from './styles/FloatingLocalVideo.styles'; import { SHORT_VERTICAL_GALLERY_FLOATING_MODAL_SIZE_REM, VERTICAL_GALLERY_FLOATING_MODAL_SIZE_REM } from './styles/FloatingLocalVideo.styles'; import { innerLayoutStyle, layerHostStyle, rootLayoutStyle } from './styles/FloatingLocalVideoLayout.styles'; import { videoGalleryLayoutGap } from './styles/Layout.styles'; import { MAX_GRID_PARTICIPANTS_NOT_LARGE_GALLERY, renderTiles, useOrganizedParticipants } from './utils/videoGalleryLayoutUtils'; import { OverflowGallery } from './OverflowGallery'; /** * FloatingLocalVideoLayout displays remote participants and a screen sharing component in * a grid and overflow gallery while floating the local video * * @private */ export const FloatingLocalVideoLayout = (props) => { const { remoteParticipants = [], dominantSpeakers, localVideoComponent, screenShareComponent, onRenderRemoteParticipant, styles, maxRemoteVideoStreams, showCameraSwitcherInLocalPreview, parentWidth, parentHeight, overflowGalleryPosition = 'horizontalBottom', pinnedParticipantUserIds = [], localVideoTileSize, spotlightedParticipantUserIds } = props; const theme = useTheme(); const isNarrow = parentWidth ? isNarrowWidth(parentWidth) : false; 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 remoteVideosOn = remoteParticipants.filter((p) => { var _a; return (_a = p.videoStream) === null || _a === void 0 ? void 0 : _a.isAvailable; }).length > 0; const { gridParticipants, overflowGalleryParticipants } = useOrganizedParticipants({ remoteParticipants, dominantSpeakers, maxGridParticipants: remoteVideosOn ? maxRemoteVideoStreams : MAX_GRID_PARTICIPANTS_NOT_LARGE_GALLERY, isScreenShareActive: !!screenShareComponent, maxOverflowGalleryDominantSpeakers: screenShareComponent ? childrenPerPage.current - (pinnedParticipantUserIds.length % childrenPerPage.current) : childrenPerPage.current, pinnedParticipantUserIds, layout: 'floatingLocalVideo', spotlightedParticipantUserIds }); /** * 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([]); const { gridTiles, overflowGalleryTiles } = renderTiles(gridParticipants, onRenderRemoteParticipant, maxRemoteVideoStreams, indexesToRender, overflowGalleryParticipants, dominantSpeakers); const shouldFloatLocalVideo = remoteParticipants.length > 0; if (!shouldFloatLocalVideo && localVideoComponent) { gridTiles.push(localVideoComponent); } const layerHostId = useId('layerhost'); const localVideoSizeRem = useMemo(() => { if ((isNarrow && localVideoTileSize !== '16:9') || localVideoTileSize === '9:16') { return SMALL_FLOATING_MODAL_SIZE_REM; } 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; } 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, isShort, overflowGalleryPosition, localVideoTileSize ]); const wrappedLocalVideoComponent = (localVideoComponent && shouldFloatLocalVideo) || (screenShareComponent && localVideoComponent) ? ( // When we use showCameraSwitcherInLocalPreview it disables dragging to allow keyboard navigation. showCameraSwitcherInLocalPreview ? (React.createElement(Stack, { className: mergeStyles(localVideoTileWithControlsContainerStyle(theme, localVideoSizeRem), { boxShadow: theme.effects.elevation8, zIndex: LOCAL_VIDEO_TILE_ZINDEX }) }, localVideoComponent)) : overflowGalleryTiles.length > 0 || screenShareComponent ? (React.createElement(Stack, { className: mergeStyles(localVideoTileContainerStyle(theme, localVideoSizeRem, !!screenShareComponent, overflowGalleryPosition)) }, localVideoComponent)) : (React.createElement(FloatingLocalVideo, { localVideoComponent: localVideoComponent, layerHostId: layerHostId, localVideoSizeRem: localVideoSizeRem, parentWidth: parentWidth, parentHeight: parentHeight }))) : undefined; const overflowGallery = useMemo(() => { if (overflowGalleryTiles.length === 0 && !screenShareComponent) { return null; } return (React.createElement(OverflowGallery, { isShort: isShort, onFetchTilesToRender: setIndexesToRender, isNarrow: isNarrow, shouldFloatLocalVideo: !!localVideoComponent, overflowGalleryElements: overflowGalleryTiles, horizontalGalleryStyles: styles === null || styles === void 0 ? void 0 : styles.horizontalGallery, verticalGalleryStyles: styles === null || styles === void 0 ? void 0 : styles.verticalGallery, overflowGalleryPosition: overflowGalleryPosition, onChildrenPerPageChange: (n) => { childrenPerPage.current = n; }, parentWidth: parentWidth })); }, [ isNarrow, isShort, screenShareComponent, overflowGalleryTiles, styles === null || styles === void 0 ? void 0 : styles.horizontalGallery, overflowGalleryPosition, setIndexesToRender, styles === null || styles === void 0 ? void 0 : styles.verticalGallery, parentWidth, localVideoComponent ]); return (React.createElement(Stack, { styles: rootLayoutStyle }, React.createElement(Stack, { horizontal: overflowGalleryPosition === 'verticalRight', styles: innerLayoutStyle, tokens: videoGalleryLayoutGap }, props.overflowGalleryPosition === 'horizontalTop' ? overflowGallery : React.createElement(React.Fragment, null), screenShareComponent ? (screenShareComponent) : (React.createElement(GridLayout, { key: "grid-layout", styles: styles === null || styles === void 0 ? void 0 : styles.gridLayout }, gridTiles)), overflowGalleryTrampoline(overflowGallery, props.overflowGalleryPosition)), React.createElement(LayerHost, { id: layerHostId, className: mergeStyles(layerHostStyle) }), wrappedLocalVideoComponent)); }; const overflowGalleryTrampoline = (gallery, galleryPosition) => { return galleryPosition !== 'horizontalTop' ? gallery : React.createElement(React.Fragment, null); }; //# sourceMappingURL=FloatingLocalVideoLayout.js.map