@stream-io/video-react-sdk 0.3.28 → 0.3.29
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.
- package/CHANGELOG.md +7 -0
- package/dist/src/core/components/CallLayout/PaginatedGridLayout.js +2 -2
- package/dist/src/core/components/CallLayout/PaginatedGridLayout.js.map +1 -1
- package/dist/src/core/components/CallLayout/SpeakerLayout.d.ts +6 -1
- package/dist/src/core/components/CallLayout/SpeakerLayout.js +28 -12
- package/dist/src/core/components/CallLayout/SpeakerLayout.js.map +1 -1
- package/dist/src/core/hooks/useCalculateHardLimit.d.ts +4 -0
- package/dist/src/core/hooks/useCalculateHardLimit.js +56 -0
- package/dist/src/core/hooks/useCalculateHardLimit.js.map +1 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
- package/src/core/components/CallLayout/PaginatedGridLayout.tsx +28 -32
- package/src/core/components/CallLayout/SpeakerLayout.tsx +71 -21
- package/src/core/hooks/useCalculateHardLimit.ts +72 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
This file was generated using [@jscutlery/semver](https://github.com/jscutlery/semver).
|
|
4
4
|
|
|
5
|
+
### [0.3.29](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-react-sdk-0.3.28...@stream-io/video-react-sdk-0.3.29) (2023-09-27)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
|
|
10
|
+
* **SpeakerLayout:** add participantsBarLimit ([#1090](https://github.com/GetStream/stream-video-js/issues/1090)) ([712f1e7](https://github.com/GetStream/stream-video-js/commit/712f1e7010fdb8859aaa6caba7e7d9e0f4557ccb))
|
|
11
|
+
|
|
5
12
|
### [0.3.28](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-react-sdk-0.3.27...@stream-io/video-react-sdk-0.3.28) (2023-09-26)
|
|
6
13
|
|
|
7
14
|
### Dependency Updates
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect, useMemo, useState } from 'react';
|
|
3
3
|
import { useCall, useCallStateHooks } from '@stream-io/video-react-bindings';
|
|
4
4
|
import clsx from 'clsx';
|
|
@@ -33,6 +33,6 @@ export const PaginatedGridLayout = ({ groupSize = GROUP_SIZE, excludeLocalPartic
|
|
|
33
33
|
const selectedGroup = participantGroups[page];
|
|
34
34
|
if (!call)
|
|
35
35
|
return null;
|
|
36
|
-
return (_jsxs(
|
|
36
|
+
return (_jsxs("div", Object.assign({ className: "str-video__paginated-grid-layout__wrapper" }, { children: [remoteParticipants.map((participant) => (_jsx(Audio, { participant: participant }, participant.sessionId))), _jsxs("div", Object.assign({ className: "str-video__paginated-grid-layout" }, { children: [pageArrowsVisible && pageCount > 1 && (_jsx(IconButton, { icon: "caret-left", disabled: page === 0, onClick: () => setPage((currentPage) => Math.max(0, currentPage - 1)) })), selectedGroup && (_jsx(PaginatedGridLayoutGroup, { group: participantGroups[page], VideoPlaceholder: VideoPlaceholder, ParticipantViewUI: ParticipantViewUI })), pageArrowsVisible && pageCount > 1 && (_jsx(IconButton, { disabled: page === pageCount - 1, icon: "caret-right", onClick: () => setPage((currentPage) => Math.min(pageCount - 1, currentPage + 1)) }))] }))] })));
|
|
37
37
|
};
|
|
38
38
|
//# sourceMappingURL=PaginatedGridLayout.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PaginatedGridLayout.js","sourceRoot":"","sources":["../../../../../src/core/components/CallLayout/PaginatedGridLayout.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACrD,OAAO,EAAE,OAAO,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AAK7E,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB,OAAO,EACL,wBAAwB,EACxB,eAAe,GAEhB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC;AACjC,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACjD,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAE3C,MAAM,UAAU,GAAG,EAAE,CAAC;AAUtB,MAAM,wBAAwB,GAAG,CAAC,EAChC,KAAK,EACL,gBAAgB,EAChB,iBAAiB,GACa,EAAE,EAAE;IAClC,OAAO,CACL,4BACE,SAAS,EAAE,IAAI,CAAC,yCAAyC,EAAE;YACzD,uCAAuC,EAAE,KAAK,CAAC,MAAM,KAAK,CAAC;YAC3D,4CAA4C,EAC1C,KAAK,CAAC,MAAM,IAAI,CAAC,IAAI,KAAK,CAAC,MAAM,IAAI,CAAC;YACxC,6CAA6C,EAC3C,KAAK,CAAC,MAAM,IAAI,CAAC,IAAI,KAAK,CAAC,MAAM,IAAI,CAAC;SACzC,CAAC,gBAED,KAAK,CAAC,GAAG,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC,CAC1B,KAAC,eAAe,IAEd,WAAW,EAAE,WAAW,EACxB,SAAS,QACT,gBAAgB,EAAE,gBAAgB,EAClC,iBAAiB,EAAE,iBAAiB,IAJ/B,WAAW,CAAC,SAAS,CAK1B,CACH,CAAC,IACE,CACP,CAAC;AACJ,CAAC,CAAC;AAmBF,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,EAClC,SAAS,GAAG,UAAU,EACtB,uBAAuB,GAAG,KAAK,EAC/B,iBAAiB,GAAG,IAAI,EACxB,gBAAgB,EAChB,iBAAiB,GAAG,wBAAwB,GACnB,EAAE,EAAE;IAC7B,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;IAEpC,MAAM,IAAI,GAAG,OAAO,EAAE,CAAC;IACvB,MAAM,EAAE,eAAe,EAAE,qBAAqB,EAAE,GAAG,iBAAiB,EAAE,CAAC;IACvE,MAAM,YAAY,GAAG,eAAe,EAAE,CAAC;IACvC,gCAAgC;IAChC,MAAM,kBAAkB,GAAG,qBAAqB,EAAE,CAAC;IAEnD,qCAAqC;IACrC,MAAM,iBAAiB,GAAG,OAAO,CAC/B,GAAG,EAAE,CACH,KAAK,CACH,uBAAuB,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC,YAAY,EAC3D,SAAS,CACV,EACH,CAAC,uBAAuB,EAAE,kBAAkB,EAAE,YAAY,EAAE,SAAS,CAAC,CACvE,CAAC;IAEF,MAAM,SAAS,GAAG,iBAAiB,CAAC,MAAM,CAAC;IAE3C,4EAA4E;IAC5E,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,IAAI,GAAG,SAAS,GAAG,CAAC,EAAE;YACxB,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,SAAS,GAAG,CAAC,CAAC,CAAC,CAAC;SACrC;IACH,CAAC,EAAE,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC;IAEtB,MAAM,aAAa,GAAG,iBAAiB,CAAC,IAAI,CAAC,CAAC;IAE9C,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAC;IAEvB,OAAO,CACL,
|
|
1
|
+
{"version":3,"file":"PaginatedGridLayout.js","sourceRoot":"","sources":["../../../../../src/core/components/CallLayout/PaginatedGridLayout.tsx"],"names":[],"mappings":"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"}
|
|
@@ -7,5 +7,10 @@ export type SpeakerLayoutProps = {
|
|
|
7
7
|
* Providing `null` will hide the bar.
|
|
8
8
|
*/
|
|
9
9
|
participantsBarPosition?: 'top' | 'bottom' | 'left' | 'right' | null;
|
|
10
|
+
/**
|
|
11
|
+
* Hard limits the number of the participants rendered in the participants bar.
|
|
12
|
+
* Providing string `dynamic` will calculate hard limit based on screen width/height.
|
|
13
|
+
*/
|
|
14
|
+
participantsBarLimit?: 'dynamic' | number;
|
|
10
15
|
} & Pick<ParticipantViewProps, 'VideoPlaceholder'>;
|
|
11
|
-
export declare const SpeakerLayout: ({ ParticipantViewUIBar, ParticipantViewUISpotlight, VideoPlaceholder, participantsBarPosition, }: SpeakerLayoutProps) => import("react/jsx-runtime").JSX.Element | null;
|
|
16
|
+
export declare const SpeakerLayout: ({ ParticipantViewUIBar, ParticipantViewUISpotlight, VideoPlaceholder, participantsBarPosition, participantsBarLimit, }: SpeakerLayoutProps) => import("react/jsx-runtime").JSX.Element | null;
|
|
@@ -2,24 +2,33 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
2
2
|
import { useEffect, useState } from 'react';
|
|
3
3
|
import { CallTypes, combineComparators, defaultSortPreset, screenSharing, SfuModels, speakerLayoutSortPreset, } from '@stream-io/video-client';
|
|
4
4
|
import { useCall, useCallStateHooks } from '@stream-io/video-react-bindings';
|
|
5
|
+
import clsx from 'clsx';
|
|
5
6
|
import { DefaultParticipantViewUI, ParticipantView, } from '../ParticipantView';
|
|
6
7
|
import { IconButton } from '../../../components';
|
|
7
8
|
import { useHorizontalScrollPosition, useVerticalScrollPosition, } from '../../../hooks';
|
|
8
|
-
import
|
|
9
|
+
import { useCalculateHardLimit } from '../../hooks/useCalculateHardLimit';
|
|
10
|
+
import { Audio } from '../Audio';
|
|
9
11
|
const DefaultParticipantViewUIBar = () => (_jsx(DefaultParticipantViewUI, { menuPlacement: "top-end" }));
|
|
10
12
|
const DefaultParticipantViewUISpotlight = () => _jsx(DefaultParticipantViewUI, {});
|
|
11
|
-
export const SpeakerLayout = ({ ParticipantViewUIBar = DefaultParticipantViewUIBar, ParticipantViewUISpotlight = DefaultParticipantViewUISpotlight, VideoPlaceholder, participantsBarPosition = 'bottom', }) => {
|
|
13
|
+
export const SpeakerLayout = ({ ParticipantViewUIBar = DefaultParticipantViewUIBar, ParticipantViewUISpotlight = DefaultParticipantViewUISpotlight, VideoPlaceholder, participantsBarPosition = 'bottom', participantsBarLimit, }) => {
|
|
12
14
|
const call = useCall();
|
|
13
|
-
const { useParticipants } = useCallStateHooks();
|
|
15
|
+
const { useParticipants, useRemoteParticipants } = useCallStateHooks();
|
|
14
16
|
const [participantInSpotlight, ...otherParticipants] = useParticipants();
|
|
15
|
-
const
|
|
17
|
+
const remoteParticipants = useRemoteParticipants();
|
|
18
|
+
const [participantsBarWrapperElement, setParticipantsBarWrapperElement] = useState(null);
|
|
19
|
+
const [participantsBarElement, setParticipantsBarElement] = useState(null);
|
|
20
|
+
const [buttonsWrapperElement, setButtonsWrapperElement] = useState(null);
|
|
21
|
+
const isSpeakerScreenSharing = hasScreenShare(participantInSpotlight);
|
|
22
|
+
const hardLimit = useCalculateHardLimit(buttonsWrapperElement, participantsBarElement, participantsBarLimit);
|
|
23
|
+
const isVertical = participantsBarPosition === 'left' || participantsBarPosition === 'right';
|
|
24
|
+
const isHorizontal = participantsBarPosition === 'top' || participantsBarPosition === 'bottom';
|
|
16
25
|
const isOneOnOneCall = otherParticipants.length === 1;
|
|
17
26
|
useEffect(() => {
|
|
18
|
-
if (!
|
|
27
|
+
if (!participantsBarWrapperElement || !call)
|
|
19
28
|
return;
|
|
20
|
-
const cleanup = call.setViewport(
|
|
29
|
+
const cleanup = call.setViewport(participantsBarWrapperElement);
|
|
21
30
|
return () => cleanup();
|
|
22
|
-
}, [
|
|
31
|
+
}, [participantsBarWrapperElement, call]);
|
|
23
32
|
useEffect(() => {
|
|
24
33
|
if (!call)
|
|
25
34
|
return;
|
|
@@ -36,13 +45,20 @@ export const SpeakerLayout = ({ ParticipantViewUIBar = DefaultParticipantViewUIB
|
|
|
36
45
|
call.setSortParticipantsBy(callConfig.options.sortParticipantsBy || defaultSortPreset);
|
|
37
46
|
};
|
|
38
47
|
}, [call, isOneOnOneCall]);
|
|
48
|
+
let participantsWithAppliedLimit = otherParticipants;
|
|
49
|
+
if (typeof participantsBarLimit !== 'undefined') {
|
|
50
|
+
const hardLimitToApply = isVertical
|
|
51
|
+
? hardLimit.vertical
|
|
52
|
+
: hardLimit.horizontal;
|
|
53
|
+
participantsWithAppliedLimit = otherParticipants.slice(0,
|
|
54
|
+
// subtract 1 if speaker is sharing screen as
|
|
55
|
+
// that one is rendered independently from otherParticipants array
|
|
56
|
+
hardLimitToApply - (isSpeakerScreenSharing ? 1 : 0));
|
|
57
|
+
}
|
|
39
58
|
if (!call)
|
|
40
59
|
return null;
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
`str-video__speaker-layout--variant-${participantsBarPosition}`) }, { children: [_jsx("div", Object.assign({ className: "str-video__speaker-layout__spotlight" }, { children: participantInSpotlight && (_jsx(ParticipantView, { participant: participantInSpotlight, muteAudio: isSpeakerScreenSharing, trackType: isSpeakerScreenSharing ? 'screenShareTrack' : 'videoTrack', ParticipantViewUI: ParticipantViewUISpotlight, VideoPlaceholder: VideoPlaceholder })) })), otherParticipants.length > 0 && participantsBarPosition && (_jsxs("div", Object.assign({ className: "str-video__speaker-layout__participants-bar-buttons-wrapper" }, { children: [_jsx("div", Object.assign({ className: "str-video__speaker-layout__participants-bar-wrapper", ref: setScrollWrapper }, { children: _jsxs("div", Object.assign({ className: "str-video__speaker-layout__participants-bar" }, { children: [isSpeakerScreenSharing && (_jsx("div", Object.assign({ className: "str-video__speaker-layout__participant-tile" }, { children: _jsx(ParticipantView, { participant: participantInSpotlight, ParticipantViewUI: ParticipantViewUIBar, VideoPlaceholder: VideoPlaceholder }) }), participantInSpotlight.sessionId)), otherParticipants.map((participant) => (_jsx("div", Object.assign({ className: "str-video__speaker-layout__participant-tile" }, { children: _jsx(ParticipantView, { participant: participant, ParticipantViewUI: ParticipantViewUIBar, VideoPlaceholder: VideoPlaceholder }) }), participant.sessionId)))] })) })), (participantsBarPosition === 'left' ||
|
|
44
|
-
participantsBarPosition === 'right') && (_jsx(VerticalScrollButtons, { scrollWrapper: scrollWrapper })), (participantsBarPosition === 'top' ||
|
|
45
|
-
participantsBarPosition === 'bottom') && (_jsx(HorizontalScrollButtons, { scrollWrapper: scrollWrapper }))] })))] })) })));
|
|
60
|
+
return (_jsxs("div", Object.assign({ className: "str-video__speaker-layout__wrapper" }, { children: [remoteParticipants.map((participant) => (_jsx(Audio, { participant: participant }, participant.sessionId))), _jsxs("div", Object.assign({ className: clsx('str-video__speaker-layout', participantsBarPosition &&
|
|
61
|
+
`str-video__speaker-layout--variant-${participantsBarPosition}`) }, { children: [_jsx("div", Object.assign({ className: "str-video__speaker-layout__spotlight" }, { children: participantInSpotlight && (_jsx(ParticipantView, { participant: participantInSpotlight, muteAudio: true, trackType: isSpeakerScreenSharing ? 'screenShareTrack' : 'videoTrack', ParticipantViewUI: ParticipantViewUISpotlight, VideoPlaceholder: VideoPlaceholder })) })), participantsWithAppliedLimit.length > 0 && participantsBarPosition && (_jsxs("div", Object.assign({ ref: setButtonsWrapperElement, className: "str-video__speaker-layout__participants-bar-buttons-wrapper" }, { children: [_jsx("div", Object.assign({ className: "str-video__speaker-layout__participants-bar-wrapper", ref: setParticipantsBarWrapperElement }, { children: _jsxs("div", Object.assign({ ref: setParticipantsBarElement, className: "str-video__speaker-layout__participants-bar" }, { children: [isSpeakerScreenSharing && (_jsx("div", Object.assign({ className: "str-video__speaker-layout__participant-tile" }, { children: _jsx(ParticipantView, { participant: participantInSpotlight, ParticipantViewUI: ParticipantViewUIBar, VideoPlaceholder: VideoPlaceholder, muteAudio: true }) }), participantInSpotlight.sessionId)), participantsWithAppliedLimit.map((participant) => (_jsx("div", Object.assign({ className: "str-video__speaker-layout__participant-tile" }, { children: _jsx(ParticipantView, { participant: participant, ParticipantViewUI: ParticipantViewUIBar, VideoPlaceholder: VideoPlaceholder, muteAudio: true }) }), participant.sessionId)))] })) })), isVertical && (_jsx(VerticalScrollButtons, { scrollWrapper: participantsBarWrapperElement })), isHorizontal && (_jsx(HorizontalScrollButtons, { scrollWrapper: participantsBarWrapperElement }))] })))] }))] })));
|
|
46
62
|
};
|
|
47
63
|
const HorizontalScrollButtons = ({ scrollWrapper, }) => {
|
|
48
64
|
const scrollPosition = useHorizontalScrollPosition(scrollWrapper);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SpeakerLayout.js","sourceRoot":"","sources":["../../../../../src/core/components/CallLayout/SpeakerLayout.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"SpeakerLayout.js","sourceRoot":"","sources":["../../../../../src/core/components/CallLayout/SpeakerLayout.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react';
|
|
2
|
+
export const useCalculateHardLimit = (
|
|
3
|
+
/**
|
|
4
|
+
* Element that stretches to 100% of the whole layout component
|
|
5
|
+
*/
|
|
6
|
+
wrapperElement,
|
|
7
|
+
/**
|
|
8
|
+
* Element that directly hosts individual `ParticipantView` (or wrapper) elements
|
|
9
|
+
*/
|
|
10
|
+
hostElement, limit) => {
|
|
11
|
+
const [calculatedLimit, setCalculatedLimit] = useState({
|
|
12
|
+
vertical: typeof limit === 'number' ? limit : 1,
|
|
13
|
+
horizontal: typeof limit === 'number' ? limit : 1,
|
|
14
|
+
});
|
|
15
|
+
useEffect(() => {
|
|
16
|
+
if (!hostElement ||
|
|
17
|
+
!wrapperElement ||
|
|
18
|
+
typeof limit === 'number' ||
|
|
19
|
+
typeof limit === 'undefined')
|
|
20
|
+
return;
|
|
21
|
+
let childWidth = null;
|
|
22
|
+
let childHeight = null;
|
|
23
|
+
const resizeObserver = new ResizeObserver((entries, observer) => {
|
|
24
|
+
// this part should ideally run as little times as possible
|
|
25
|
+
// get child measurements and disconnect
|
|
26
|
+
// does not consider dynamically sized children
|
|
27
|
+
// this hook is for SpeakerLayout use only, where children in the bar are fixed size
|
|
28
|
+
if (entries.length > 1) {
|
|
29
|
+
const child = hostElement.firstChild;
|
|
30
|
+
if (child) {
|
|
31
|
+
childHeight = child.clientHeight;
|
|
32
|
+
childWidth = child.clientWidth;
|
|
33
|
+
observer.unobserve(hostElement);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
// keep the state at { vertical: 1, horizontal: 1 }
|
|
37
|
+
// until we get the proper child measurements
|
|
38
|
+
if (childHeight === null || childWidth === null)
|
|
39
|
+
return;
|
|
40
|
+
const vertical = Math.floor(wrapperElement.clientHeight / childHeight);
|
|
41
|
+
const horizontal = Math.floor(wrapperElement.clientWidth / childWidth);
|
|
42
|
+
setCalculatedLimit((pv) => {
|
|
43
|
+
if (pv.vertical !== vertical || pv.horizontal !== horizontal)
|
|
44
|
+
return { vertical, horizontal };
|
|
45
|
+
return pv;
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
resizeObserver.observe(wrapperElement);
|
|
49
|
+
resizeObserver.observe(hostElement);
|
|
50
|
+
return () => {
|
|
51
|
+
resizeObserver.disconnect();
|
|
52
|
+
};
|
|
53
|
+
}, [hostElement, limit, wrapperElement]);
|
|
54
|
+
return calculatedLimit;
|
|
55
|
+
};
|
|
56
|
+
//# sourceMappingURL=useCalculateHardLimit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useCalculateHardLimit.js","sourceRoot":"","sources":["../../../../src/core/hooks/useCalculateHardLimit.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAE5C,MAAM,CAAC,MAAM,qBAAqB,GAAG;AACnC;;GAEG;AACH,cAAqC;AACrC;;GAEG;AACH,WAAkC,EAClC,KAA0B,EAC1B,EAAE;IACF,MAAM,CAAC,eAAe,EAAE,kBAAkB,CAAC,GAAG,QAAQ,CAGnD;QACD,QAAQ,EAAE,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QAC/C,UAAU,EAAE,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;KAClD,CAAC,CAAC;IAEH,SAAS,CAAC,GAAG,EAAE;QACb,IACE,CAAC,WAAW;YACZ,CAAC,cAAc;YACf,OAAO,KAAK,KAAK,QAAQ;YACzB,OAAO,KAAK,KAAK,WAAW;YAE5B,OAAO;QAET,IAAI,UAAU,GAAkB,IAAI,CAAC;QACrC,IAAI,WAAW,GAAkB,IAAI,CAAC;QAEtC,MAAM,cAAc,GAAG,IAAI,cAAc,CAAC,CAAC,OAAO,EAAE,QAAQ,EAAE,EAAE;YAC9D,2DAA2D;YAC3D,wCAAwC;YACxC,+CAA+C;YAC/C,oFAAoF;YACpF,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE;gBACtB,MAAM,KAAK,GAAG,WAAW,CAAC,UAAgC,CAAC;gBAE3D,IAAI,KAAK,EAAE;oBACT,WAAW,GAAG,KAAK,CAAC,YAAY,CAAC;oBACjC,UAAU,GAAG,KAAK,CAAC,WAAW,CAAC;oBAC/B,QAAQ,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;iBACjC;aACF;YAED,mDAAmD;YACnD,6CAA6C;YAC7C,IAAI,WAAW,KAAK,IAAI,IAAI,UAAU,KAAK,IAAI;gBAAE,OAAO;YAExD,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,YAAY,GAAG,WAAW,CAAC,CAAC;YACvE,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,WAAW,GAAG,UAAU,CAAC,CAAC;YAEvE,kBAAkB,CAAC,CAAC,EAAE,EAAE,EAAE;gBACxB,IAAI,EAAE,CAAC,QAAQ,KAAK,QAAQ,IAAI,EAAE,CAAC,UAAU,KAAK,UAAU;oBAC1D,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC;gBAClC,OAAO,EAAE,CAAC;YACZ,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,cAAc,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC;QACvC,cAAc,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;QAEpC,OAAO,GAAG,EAAE;YACV,cAAc,CAAC,UAAU,EAAE,CAAC;QAC9B,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,WAAW,EAAE,KAAK,EAAE,cAAc,CAAC,CAAC,CAAC;IAEzC,OAAO,eAAe,CAAC;AACzB,CAAC,CAAC"}
|
package/dist/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const version = "0.3.
|
|
1
|
+
export declare const version = "0.3.29";
|
package/dist/version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export const version = '0.3.
|
|
1
|
+
export const version = '0.3.29';
|
|
2
2
|
//# sourceMappingURL=version.js.map
|
package/package.json
CHANGED
|
@@ -109,41 +109,37 @@ export const PaginatedGridLayout = ({
|
|
|
109
109
|
if (!call) return null;
|
|
110
110
|
|
|
111
111
|
return (
|
|
112
|
-
|
|
112
|
+
<div className="str-video__paginated-grid-layout__wrapper">
|
|
113
113
|
{remoteParticipants.map((participant) => (
|
|
114
114
|
<Audio key={participant.sessionId} participant={participant} />
|
|
115
115
|
))}
|
|
116
|
-
<div className="str-video__paginated-grid-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
}
|
|
143
|
-
/>
|
|
144
|
-
)}
|
|
145
|
-
</div>
|
|
116
|
+
<div className="str-video__paginated-grid-layout">
|
|
117
|
+
{pageArrowsVisible && pageCount > 1 && (
|
|
118
|
+
<IconButton
|
|
119
|
+
icon="caret-left"
|
|
120
|
+
disabled={page === 0}
|
|
121
|
+
onClick={() =>
|
|
122
|
+
setPage((currentPage) => Math.max(0, currentPage - 1))
|
|
123
|
+
}
|
|
124
|
+
/>
|
|
125
|
+
)}
|
|
126
|
+
{selectedGroup && (
|
|
127
|
+
<PaginatedGridLayoutGroup
|
|
128
|
+
group={participantGroups[page]}
|
|
129
|
+
VideoPlaceholder={VideoPlaceholder}
|
|
130
|
+
ParticipantViewUI={ParticipantViewUI}
|
|
131
|
+
/>
|
|
132
|
+
)}
|
|
133
|
+
{pageArrowsVisible && pageCount > 1 && (
|
|
134
|
+
<IconButton
|
|
135
|
+
disabled={page === pageCount - 1}
|
|
136
|
+
icon="caret-right"
|
|
137
|
+
onClick={() =>
|
|
138
|
+
setPage((currentPage) => Math.min(pageCount - 1, currentPage + 1))
|
|
139
|
+
}
|
|
140
|
+
/>
|
|
141
|
+
)}
|
|
146
142
|
</div>
|
|
147
|
-
|
|
143
|
+
</div>
|
|
148
144
|
);
|
|
149
145
|
};
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { useEffect, useState } from 'react';
|
|
2
|
-
|
|
3
2
|
import {
|
|
4
3
|
CallTypes,
|
|
5
4
|
combineComparators,
|
|
@@ -11,6 +10,7 @@ import {
|
|
|
11
10
|
StreamVideoParticipant,
|
|
12
11
|
} from '@stream-io/video-client';
|
|
13
12
|
import { useCall, useCallStateHooks } from '@stream-io/video-react-bindings';
|
|
13
|
+
import clsx from 'clsx';
|
|
14
14
|
|
|
15
15
|
import {
|
|
16
16
|
DefaultParticipantViewUI,
|
|
@@ -22,7 +22,8 @@ import {
|
|
|
22
22
|
useHorizontalScrollPosition,
|
|
23
23
|
useVerticalScrollPosition,
|
|
24
24
|
} from '../../../hooks';
|
|
25
|
-
import
|
|
25
|
+
import { useCalculateHardLimit } from '../../hooks/useCalculateHardLimit';
|
|
26
|
+
import { Audio } from '../Audio';
|
|
26
27
|
|
|
27
28
|
export type SpeakerLayoutProps = {
|
|
28
29
|
ParticipantViewUISpotlight?: ParticipantViewProps['ParticipantViewUI'];
|
|
@@ -32,6 +33,11 @@ export type SpeakerLayoutProps = {
|
|
|
32
33
|
* Providing `null` will hide the bar.
|
|
33
34
|
*/
|
|
34
35
|
participantsBarPosition?: 'top' | 'bottom' | 'left' | 'right' | null;
|
|
36
|
+
/**
|
|
37
|
+
* Hard limits the number of the participants rendered in the participants bar.
|
|
38
|
+
* Providing string `dynamic` will calculate hard limit based on screen width/height.
|
|
39
|
+
*/
|
|
40
|
+
participantsBarLimit?: 'dynamic' | number;
|
|
35
41
|
} & Pick<ParticipantViewProps, 'VideoPlaceholder'>;
|
|
36
42
|
|
|
37
43
|
const DefaultParticipantViewUIBar = () => (
|
|
@@ -45,21 +51,38 @@ export const SpeakerLayout = ({
|
|
|
45
51
|
ParticipantViewUISpotlight = DefaultParticipantViewUISpotlight,
|
|
46
52
|
VideoPlaceholder,
|
|
47
53
|
participantsBarPosition = 'bottom',
|
|
54
|
+
participantsBarLimit,
|
|
48
55
|
}: SpeakerLayoutProps) => {
|
|
49
56
|
const call = useCall();
|
|
50
|
-
const { useParticipants } = useCallStateHooks();
|
|
57
|
+
const { useParticipants, useRemoteParticipants } = useCallStateHooks();
|
|
51
58
|
const [participantInSpotlight, ...otherParticipants] = useParticipants();
|
|
52
|
-
const
|
|
53
|
-
|
|
59
|
+
const remoteParticipants = useRemoteParticipants();
|
|
60
|
+
const [participantsBarWrapperElement, setParticipantsBarWrapperElement] =
|
|
61
|
+
useState<HTMLDivElement | null>(null);
|
|
62
|
+
const [participantsBarElement, setParticipantsBarElement] =
|
|
63
|
+
useState<HTMLDivElement | null>(null);
|
|
64
|
+
const [buttonsWrapperElement, setButtonsWrapperElement] =
|
|
65
|
+
useState<HTMLDivElement | null>(null);
|
|
66
|
+
|
|
67
|
+
const isSpeakerScreenSharing = hasScreenShare(participantInSpotlight);
|
|
68
|
+
const hardLimit = useCalculateHardLimit(
|
|
69
|
+
buttonsWrapperElement,
|
|
70
|
+
participantsBarElement,
|
|
71
|
+
participantsBarLimit,
|
|
54
72
|
);
|
|
73
|
+
|
|
74
|
+
const isVertical =
|
|
75
|
+
participantsBarPosition === 'left' || participantsBarPosition === 'right';
|
|
76
|
+
const isHorizontal =
|
|
77
|
+
participantsBarPosition === 'top' || participantsBarPosition === 'bottom';
|
|
55
78
|
const isOneOnOneCall = otherParticipants.length === 1;
|
|
56
79
|
|
|
57
80
|
useEffect(() => {
|
|
58
|
-
if (!
|
|
81
|
+
if (!participantsBarWrapperElement || !call) return;
|
|
59
82
|
|
|
60
|
-
const cleanup = call.setViewport(
|
|
83
|
+
const cleanup = call.setViewport(participantsBarWrapperElement);
|
|
61
84
|
return () => cleanup();
|
|
62
|
-
}, [
|
|
85
|
+
}, [participantsBarWrapperElement, call]);
|
|
63
86
|
|
|
64
87
|
useEffect(() => {
|
|
65
88
|
if (!call) return;
|
|
@@ -79,11 +102,28 @@ export const SpeakerLayout = ({
|
|
|
79
102
|
};
|
|
80
103
|
}, [call, isOneOnOneCall]);
|
|
81
104
|
|
|
105
|
+
let participantsWithAppliedLimit = otherParticipants;
|
|
106
|
+
|
|
107
|
+
if (typeof participantsBarLimit !== 'undefined') {
|
|
108
|
+
const hardLimitToApply = isVertical
|
|
109
|
+
? hardLimit.vertical
|
|
110
|
+
: hardLimit.horizontal;
|
|
111
|
+
|
|
112
|
+
participantsWithAppliedLimit = otherParticipants.slice(
|
|
113
|
+
0,
|
|
114
|
+
// subtract 1 if speaker is sharing screen as
|
|
115
|
+
// that one is rendered independently from otherParticipants array
|
|
116
|
+
hardLimitToApply - (isSpeakerScreenSharing ? 1 : 0),
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
|
|
82
120
|
if (!call) return null;
|
|
83
121
|
|
|
84
|
-
const isSpeakerScreenSharing = hasScreenShare(participantInSpotlight);
|
|
85
122
|
return (
|
|
86
123
|
<div className="str-video__speaker-layout__wrapper">
|
|
124
|
+
{remoteParticipants.map((participant) => (
|
|
125
|
+
<Audio key={participant.sessionId} participant={participant} />
|
|
126
|
+
))}
|
|
87
127
|
<div
|
|
88
128
|
className={clsx(
|
|
89
129
|
'str-video__speaker-layout',
|
|
@@ -95,7 +135,7 @@ export const SpeakerLayout = ({
|
|
|
95
135
|
{participantInSpotlight && (
|
|
96
136
|
<ParticipantView
|
|
97
137
|
participant={participantInSpotlight}
|
|
98
|
-
muteAudio={
|
|
138
|
+
muteAudio={true}
|
|
99
139
|
trackType={
|
|
100
140
|
isSpeakerScreenSharing ? 'screenShareTrack' : 'videoTrack'
|
|
101
141
|
}
|
|
@@ -104,13 +144,19 @@ export const SpeakerLayout = ({
|
|
|
104
144
|
/>
|
|
105
145
|
)}
|
|
106
146
|
</div>
|
|
107
|
-
{
|
|
108
|
-
<div
|
|
147
|
+
{participantsWithAppliedLimit.length > 0 && participantsBarPosition && (
|
|
148
|
+
<div
|
|
149
|
+
ref={setButtonsWrapperElement}
|
|
150
|
+
className="str-video__speaker-layout__participants-bar-buttons-wrapper"
|
|
151
|
+
>
|
|
109
152
|
<div
|
|
110
153
|
className="str-video__speaker-layout__participants-bar-wrapper"
|
|
111
|
-
ref={
|
|
154
|
+
ref={setParticipantsBarWrapperElement}
|
|
112
155
|
>
|
|
113
|
-
<div
|
|
156
|
+
<div
|
|
157
|
+
ref={setParticipantsBarElement}
|
|
158
|
+
className="str-video__speaker-layout__participants-bar"
|
|
159
|
+
>
|
|
114
160
|
{isSpeakerScreenSharing && (
|
|
115
161
|
<div
|
|
116
162
|
className="str-video__speaker-layout__participant-tile"
|
|
@@ -120,10 +166,11 @@ export const SpeakerLayout = ({
|
|
|
120
166
|
participant={participantInSpotlight}
|
|
121
167
|
ParticipantViewUI={ParticipantViewUIBar}
|
|
122
168
|
VideoPlaceholder={VideoPlaceholder}
|
|
169
|
+
muteAudio={true}
|
|
123
170
|
/>
|
|
124
171
|
</div>
|
|
125
172
|
)}
|
|
126
|
-
{
|
|
173
|
+
{participantsWithAppliedLimit.map((participant) => (
|
|
127
174
|
<div
|
|
128
175
|
className="str-video__speaker-layout__participant-tile"
|
|
129
176
|
key={participant.sessionId}
|
|
@@ -132,18 +179,21 @@ export const SpeakerLayout = ({
|
|
|
132
179
|
participant={participant}
|
|
133
180
|
ParticipantViewUI={ParticipantViewUIBar}
|
|
134
181
|
VideoPlaceholder={VideoPlaceholder}
|
|
182
|
+
muteAudio={true}
|
|
135
183
|
/>
|
|
136
184
|
</div>
|
|
137
185
|
))}
|
|
138
186
|
</div>
|
|
139
187
|
</div>
|
|
140
|
-
{
|
|
141
|
-
|
|
142
|
-
|
|
188
|
+
{isVertical && (
|
|
189
|
+
<VerticalScrollButtons
|
|
190
|
+
scrollWrapper={participantsBarWrapperElement}
|
|
191
|
+
/>
|
|
143
192
|
)}
|
|
144
|
-
{
|
|
145
|
-
|
|
146
|
-
|
|
193
|
+
{isHorizontal && (
|
|
194
|
+
<HorizontalScrollButtons
|
|
195
|
+
scrollWrapper={participantsBarWrapperElement}
|
|
196
|
+
/>
|
|
147
197
|
)}
|
|
148
198
|
</div>
|
|
149
199
|
)}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react';
|
|
2
|
+
|
|
3
|
+
export const useCalculateHardLimit = (
|
|
4
|
+
/**
|
|
5
|
+
* Element that stretches to 100% of the whole layout component
|
|
6
|
+
*/
|
|
7
|
+
wrapperElement: HTMLDivElement | null,
|
|
8
|
+
/**
|
|
9
|
+
* Element that directly hosts individual `ParticipantView` (or wrapper) elements
|
|
10
|
+
*/
|
|
11
|
+
hostElement: HTMLDivElement | null,
|
|
12
|
+
limit?: 'dynamic' | number,
|
|
13
|
+
) => {
|
|
14
|
+
const [calculatedLimit, setCalculatedLimit] = useState<{
|
|
15
|
+
vertical: number;
|
|
16
|
+
horizontal: number;
|
|
17
|
+
}>({
|
|
18
|
+
vertical: typeof limit === 'number' ? limit : 1,
|
|
19
|
+
horizontal: typeof limit === 'number' ? limit : 1,
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
if (
|
|
24
|
+
!hostElement ||
|
|
25
|
+
!wrapperElement ||
|
|
26
|
+
typeof limit === 'number' ||
|
|
27
|
+
typeof limit === 'undefined'
|
|
28
|
+
)
|
|
29
|
+
return;
|
|
30
|
+
|
|
31
|
+
let childWidth: number | null = null;
|
|
32
|
+
let childHeight: number | null = null;
|
|
33
|
+
|
|
34
|
+
const resizeObserver = new ResizeObserver((entries, observer) => {
|
|
35
|
+
// this part should ideally run as little times as possible
|
|
36
|
+
// get child measurements and disconnect
|
|
37
|
+
// does not consider dynamically sized children
|
|
38
|
+
// this hook is for SpeakerLayout use only, where children in the bar are fixed size
|
|
39
|
+
if (entries.length > 1) {
|
|
40
|
+
const child = hostElement.firstChild as HTMLElement | null;
|
|
41
|
+
|
|
42
|
+
if (child) {
|
|
43
|
+
childHeight = child.clientHeight;
|
|
44
|
+
childWidth = child.clientWidth;
|
|
45
|
+
observer.unobserve(hostElement);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// keep the state at { vertical: 1, horizontal: 1 }
|
|
50
|
+
// until we get the proper child measurements
|
|
51
|
+
if (childHeight === null || childWidth === null) return;
|
|
52
|
+
|
|
53
|
+
const vertical = Math.floor(wrapperElement.clientHeight / childHeight);
|
|
54
|
+
const horizontal = Math.floor(wrapperElement.clientWidth / childWidth);
|
|
55
|
+
|
|
56
|
+
setCalculatedLimit((pv) => {
|
|
57
|
+
if (pv.vertical !== vertical || pv.horizontal !== horizontal)
|
|
58
|
+
return { vertical, horizontal };
|
|
59
|
+
return pv;
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
resizeObserver.observe(wrapperElement);
|
|
64
|
+
resizeObserver.observe(hostElement);
|
|
65
|
+
|
|
66
|
+
return () => {
|
|
67
|
+
resizeObserver.disconnect();
|
|
68
|
+
};
|
|
69
|
+
}, [hostElement, limit, wrapperElement]);
|
|
70
|
+
|
|
71
|
+
return calculatedLimit;
|
|
72
|
+
};
|