@whereby.com/browser-sdk 3.9.1 → 3.9.2

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.
@@ -0,0 +1,2251 @@
1
+ 'use strict';
2
+
3
+ var React = require('react');
4
+ var reactRedux = require('react-redux');
5
+ var core = require('@whereby.com/core');
6
+ var toolkit = require('@reduxjs/toolkit');
7
+ var runes = require('runes');
8
+ var PopoverPrimitive = require('@radix-ui/react-popover');
9
+
10
+ function _interopNamespaceDefault(e) {
11
+ var n = Object.create(null);
12
+ if (e) {
13
+ Object.keys(e).forEach(function (k) {
14
+ if (k !== 'default') {
15
+ var d = Object.getOwnPropertyDescriptor(e, k);
16
+ Object.defineProperty(n, k, d.get ? d : {
17
+ enumerable: true,
18
+ get: function () { return e[k]; }
19
+ });
20
+ }
21
+ });
22
+ }
23
+ n.default = e;
24
+ return Object.freeze(n);
25
+ }
26
+
27
+ var React__namespace = /*#__PURE__*/_interopNamespaceDefault(React);
28
+ var PopoverPrimitive__namespace = /*#__PURE__*/_interopNamespaceDefault(PopoverPrimitive);
29
+
30
+ function Provider({ children }) {
31
+ const services = core.createServices();
32
+ const store = core.createStore({ injectServices: services });
33
+ return React__namespace.createElement(reactRedux.Provider, { store: store }, children);
34
+ }
35
+
36
+ /******************************************************************************
37
+ Copyright (c) Microsoft Corporation.
38
+
39
+ Permission to use, copy, modify, and/or distribute this software for any
40
+ purpose with or without fee is hereby granted.
41
+
42
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
43
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
44
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
45
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
46
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
47
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
48
+ PERFORMANCE OF THIS SOFTWARE.
49
+ ***************************************************************************** */
50
+ /* global Reflect, Promise, SuppressedError, Symbol */
51
+
52
+
53
+ function __rest(s, e) {
54
+ var t = {};
55
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
56
+ t[p] = s[p];
57
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
58
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
59
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
60
+ t[p[i]] = s[p[i]];
61
+ }
62
+ return t;
63
+ }
64
+
65
+ typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
66
+ var e = new Error(message);
67
+ return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
68
+ };
69
+
70
+ const useAppDispatch = reactRedux.useDispatch.withTypes();
71
+ const useAppSelector = reactRedux.useSelector.withTypes();
72
+
73
+ const VideoView = React__namespace.forwardRef((_a, ref) => {
74
+ var { muted, mirror = false, stream, onVideoResize } = _a, rest = __rest(_a, ["muted", "mirror", "stream", "onVideoResize"]);
75
+ const dispatch = useAppDispatch();
76
+ const currentSpeakerId = useAppSelector(core.selectCurrentSpeakerDeviceId);
77
+ const videoEl = React__namespace.useRef(null);
78
+ const audioEl = React__namespace.useRef(null);
79
+ React__namespace.useImperativeHandle(ref, () => {
80
+ return Object.assign(videoEl.current, {
81
+ captureAspectRatio: () => {
82
+ if (!videoEl.current) {
83
+ return null;
84
+ }
85
+ const h = videoEl.current.videoHeight;
86
+ const w = videoEl.current.videoWidth;
87
+ if (w && h && w + h > 20) {
88
+ return w / h;
89
+ }
90
+ return null;
91
+ },
92
+ });
93
+ });
94
+ React__namespace.useEffect(() => {
95
+ if (!videoEl.current) {
96
+ return;
97
+ }
98
+ const resizeObserver = new ResizeObserver(core.debounce(() => {
99
+ if (videoEl.current && (stream === null || stream === void 0 ? void 0 : stream.id)) {
100
+ const width = videoEl.current.clientWidth;
101
+ const height = videoEl.current.clientHeight;
102
+ dispatch(core.doRtcReportStreamResolution({
103
+ streamId: stream.id,
104
+ width,
105
+ height,
106
+ }));
107
+ if (onVideoResize) {
108
+ onVideoResize({
109
+ width,
110
+ height,
111
+ stream,
112
+ });
113
+ }
114
+ }
115
+ }, { delay: 1000, edges: true }));
116
+ resizeObserver.observe(videoEl.current);
117
+ return () => {
118
+ resizeObserver.disconnect();
119
+ };
120
+ }, [stream]);
121
+ React__namespace.useEffect(() => {
122
+ if (!videoEl.current) {
123
+ return;
124
+ }
125
+ if (videoEl.current.srcObject !== stream) {
126
+ videoEl.current.srcObject = stream;
127
+ }
128
+ if (videoEl.current.muted !== muted) {
129
+ videoEl.current.muted = Boolean(muted);
130
+ }
131
+ }, [muted, stream, videoEl]);
132
+ React__namespace.useEffect(() => {
133
+ if (!audioEl.current || muted || !stream || !currentSpeakerId) {
134
+ return;
135
+ }
136
+ if (audioEl.current.srcObject !== stream) {
137
+ audioEl.current.srcObject = stream;
138
+ }
139
+ if (audioEl.current.setSinkId) {
140
+ audioEl.current.setSinkId(currentSpeakerId);
141
+ }
142
+ }, [stream, audioEl, currentSpeakerId, muted]);
143
+ return (React__namespace.createElement(React__namespace.Fragment, null,
144
+ React__namespace.createElement("video", Object.assign({ ref: videoEl, autoPlay: true, playsInline: true }, rest, { style: Object.assign({ transform: mirror ? "scaleX(-1)" : "none", width: "100%", height: "100%" }, rest.style) })),
145
+ React__namespace.createElement("audio", { ref: audioEl, autoPlay: true, playsInline: true })));
146
+ });
147
+ VideoView.displayName = "VideoView";
148
+
149
+ const selectRoomConnectionState = toolkit.createSelector(core.selectChatMessages, core.selectCloudRecordingRaw, core.selectLocalParticipantRaw, core.selectLocalMediaStream, core.selectRemoteParticipants, core.selectScreenshares, core.selectRoomConnectionStatus, core.selectStreamingRaw, core.selectWaitingParticipants, core.selectNotificationsEmitter, core.selectSpotlightedClientViews, (chatMessages, cloudRecording, localParticipant, localMediaStream, remoteParticipants, screenshares, connectionStatus, streaming, waitingParticipants, notificationsEmitter, spotlightedClientViews) => {
150
+ const state = {
151
+ chatMessages,
152
+ cloudRecording: cloudRecording.isRecording ? { status: "recording" } : undefined,
153
+ connectionStatus,
154
+ events: notificationsEmitter,
155
+ liveStream: streaming.isStreaming
156
+ ? {
157
+ status: "streaming",
158
+ startedAt: streaming.startedAt,
159
+ }
160
+ : undefined,
161
+ localScreenshareStatus: localParticipant.isScreenSharing ? "active" : undefined,
162
+ localParticipant: Object.assign(Object.assign({}, localParticipant), { stream: localMediaStream }),
163
+ remoteParticipants,
164
+ screenshares,
165
+ waitingParticipants,
166
+ spotlightedParticipants: spotlightedClientViews,
167
+ };
168
+ return state;
169
+ });
170
+
171
+ const browserSdkVersion = "3.9.2";
172
+
173
+ const defaultRoomConnectionOptions = {
174
+ localMediaOptions: {
175
+ audio: true,
176
+ video: true,
177
+ },
178
+ };
179
+ function useRoomConnection(roomUrl, roomConnectionOptions = defaultRoomConnectionOptions) {
180
+ const dispatch = useAppDispatch();
181
+ const roomConnectionState = useAppSelector(selectRoomConnectionState);
182
+ const roomConfig = React__namespace.useMemo(() => {
183
+ const url = new URL(roomUrl);
184
+ const searchParams = new URLSearchParams(url.search);
185
+ const roomKey = roomConnectionOptions.roomKey || searchParams.get("roomKey");
186
+ return {
187
+ displayName: roomConnectionOptions.displayName || "Guest",
188
+ localMediaOptions: roomConnectionOptions.localMedia ? undefined : roomConnectionOptions.localMediaOptions,
189
+ roomKey,
190
+ roomUrl,
191
+ userAgent: `browser-sdk:${browserSdkVersion}`,
192
+ externalId: roomConnectionOptions.externalId || null,
193
+ };
194
+ }, [roomUrl, roomConnectionOptions]);
195
+ React__namespace.useEffect(() => {
196
+ return () => {
197
+ dispatch(core.doAppStop());
198
+ };
199
+ }, []);
200
+ const sendChatMessage = React__namespace.useCallback((text) => dispatch(core.doSendChatMessage({ text })), [dispatch]);
201
+ const knock = React__namespace.useCallback(() => dispatch(core.doKnockRoom()), [dispatch]);
202
+ const setDisplayName = React__namespace.useCallback((displayName) => dispatch(core.doSetDisplayName({ displayName })), [dispatch]);
203
+ const toggleCamera = React__namespace.useCallback((enabled) => dispatch(core.toggleCameraEnabled({ enabled })), [dispatch]);
204
+ const toggleMicrophone = React__namespace.useCallback((enabled) => dispatch(core.toggleMicrophoneEnabled({ enabled })), [dispatch]);
205
+ const toggleLowDataMode = React__namespace.useCallback((enabled) => dispatch(core.toggleLowDataModeEnabled({ enabled })), [dispatch]);
206
+ const toggleRaiseHand = React__namespace.useCallback((enabled) => dispatch(core.doSetLocalStickyReaction({ enabled })), [dispatch]);
207
+ const askToSpeak = React__namespace.useCallback((participantId) => dispatch(core.doRequestAudioEnable({ clientIds: [participantId], enable: true })), [dispatch]);
208
+ const askToTurnOnCamera = React__namespace.useCallback((participantId) => dispatch(core.doRequestVideoEnable({ clientIds: [participantId], enable: true })), [dispatch]);
209
+ const acceptWaitingParticipant = React__namespace.useCallback((participantId) => dispatch(core.doAcceptWaitingParticipant({ participantId })), [dispatch]);
210
+ const rejectWaitingParticipant = React__namespace.useCallback((participantId) => dispatch(core.doRejectWaitingParticipant({ participantId })), [dispatch]);
211
+ const startCloudRecording = React__namespace.useCallback(() => dispatch(core.doStartCloudRecording()), [dispatch]);
212
+ const startScreenshare = React__namespace.useCallback(() => dispatch(core.doStartScreenshare()), [dispatch]);
213
+ const stopCloudRecording = React__namespace.useCallback(() => dispatch(core.doStopCloudRecording()), [dispatch]);
214
+ const stopScreenshare = React__namespace.useCallback(() => dispatch(core.doStopScreenshare()), [dispatch]);
215
+ const joinRoom = React__namespace.useCallback(() => dispatch(core.doAppStart(roomConfig)), [dispatch]);
216
+ const leaveRoom = React__namespace.useCallback(() => dispatch(core.doAppStop()), [dispatch]);
217
+ const lockRoom = React__namespace.useCallback((locked) => dispatch(core.doLockRoom({ locked })), [dispatch]);
218
+ const muteParticipants = React__namespace.useCallback((participantIds) => dispatch(core.doRequestAudioEnable({ clientIds: participantIds, enable: false })), [dispatch]);
219
+ const turnOffParticipantCameras = React__namespace.useCallback((participantIds) => dispatch(core.doRequestVideoEnable({ clientIds: participantIds, enable: false })), [dispatch]);
220
+ const spotlightParticipant = React__namespace.useCallback((participantId) => dispatch(core.doSpotlightParticipant({ id: participantId })), [dispatch]);
221
+ const removeSpotlight = React__namespace.useCallback((participantId) => dispatch(core.doRemoveSpotlight({ id: participantId })), [dispatch]);
222
+ const kickParticipant = React__namespace.useCallback((participantId) => dispatch(core.doKickParticipant({ clientId: participantId })), [dispatch]);
223
+ const endMeeting = React__namespace.useCallback((stayBehind) => dispatch(core.doEndMeeting({ stayBehind })), [dispatch]);
224
+ const { events } = roomConnectionState, state = __rest(roomConnectionState, ["events"]);
225
+ return {
226
+ state,
227
+ events,
228
+ actions: {
229
+ toggleLowDataMode,
230
+ toggleRaiseHand,
231
+ askToSpeak,
232
+ askToTurnOnCamera,
233
+ acceptWaitingParticipant,
234
+ knock,
235
+ joinRoom,
236
+ leaveRoom,
237
+ lockRoom,
238
+ muteParticipants,
239
+ turnOffParticipantCameras,
240
+ kickParticipant,
241
+ endMeeting,
242
+ rejectWaitingParticipant,
243
+ sendChatMessage,
244
+ setDisplayName,
245
+ startCloudRecording,
246
+ startScreenshare,
247
+ stopCloudRecording,
248
+ stopScreenshare,
249
+ toggleCamera,
250
+ toggleMicrophone,
251
+ spotlightParticipant,
252
+ removeSpotlight,
253
+ },
254
+ };
255
+ }
256
+
257
+ const selectLocalMediaState = toolkit.createSelector(core.selectCameraDeviceError, core.selectCameraDevices, core.selectCurrentCameraDeviceId, core.selectCurrentMicrophoneDeviceId, core.selectCurrentSpeakerDeviceId, core.selectIsSettingCameraDevice, core.selectIsSettingMicrophoneDevice, core.selectIsLocalMediaStarting, core.selectLocalMediaStream, core.selectMicrophoneDeviceError, core.selectMicrophoneDevices, core.selectSpeakerDevices, core.selectLocalMediaStartError, (cameraDeviceError, cameraDevices, currentCameraDeviceId, currentMicrophoneDeviceId, currentSpeakerDeviceId, isSettingCameraDevice, isSettingMicrophoneDevice, isStarting, localStream, microphoneDeviceError, microphoneDevices, speakerDevices, startError) => {
258
+ const state = {
259
+ cameraDeviceError,
260
+ cameraDevices,
261
+ currentCameraDeviceId,
262
+ currentMicrophoneDeviceId,
263
+ currentSpeakerDeviceId,
264
+ isSettingCameraDevice,
265
+ isSettingMicrophoneDevice,
266
+ isStarting,
267
+ localStream,
268
+ microphoneDeviceError,
269
+ microphoneDevices,
270
+ speakerDevices,
271
+ startError,
272
+ };
273
+ return state;
274
+ });
275
+
276
+ function useLocalMedia(optionsOrStream = { audio: true, video: true }) {
277
+ const dispatch = useAppDispatch();
278
+ const localMediaState = useAppSelector(selectLocalMediaState);
279
+ React__namespace.useEffect(() => {
280
+ dispatch(core.doStartLocalMedia(optionsOrStream));
281
+ return () => {
282
+ dispatch(core.doStopLocalMedia());
283
+ };
284
+ }, []);
285
+ const setCameraDevice = React__namespace.useCallback((deviceId) => dispatch(core.setCurrentCameraDeviceId({ deviceId })), [dispatch]);
286
+ const setMicrophoneDevice = React__namespace.useCallback((deviceId) => dispatch(core.setCurrentMicrophoneDeviceId({ deviceId })), [dispatch]);
287
+ const setSpeakerDevice = React__namespace.useCallback((deviceId) => dispatch(core.setCurrentSpeakerDeviceId({ deviceId })), [dispatch]);
288
+ const toggleCamera = React__namespace.useCallback((enabled) => dispatch(core.toggleCameraEnabled({ enabled })), [dispatch]);
289
+ const toggleMicrophone = React__namespace.useCallback((enabled) => dispatch(core.toggleMicrophoneEnabled({ enabled })), [dispatch]);
290
+ const toggleLowDataMode = React__namespace.useCallback((enabled) => dispatch(core.toggleLowDataModeEnabled({ enabled })), [dispatch]);
291
+ return {
292
+ state: localMediaState,
293
+ actions: {
294
+ setCameraDevice,
295
+ setMicrophoneDevice,
296
+ setSpeakerDevice,
297
+ toggleCameraEnabled: toggleCamera,
298
+ toggleMicrophoneEnabled: toggleMicrophone,
299
+ toggleLowDataModeEnabled: toggleLowDataMode,
300
+ },
301
+ };
302
+ }
303
+
304
+ const VIDEO_CONTROLS_MIN_WIDTH$1 = 7 * 60;
305
+ var layoutConstants = {
306
+ MIN_WINDOW_HEIGHT: 320,
307
+ MIN_WINDOW_WIDTH: 320,
308
+ DESKTOP_BREAKPOINT: 1025,
309
+ TABLET_BREAKPOINT: 750,
310
+ PHONE_BREAKPOINT: 500,
311
+ TOP_TOOLBAR_HEIGHT: 40 + 8 * 2,
312
+ BOTTOM_TOOLBAR_HEIGHT: 70 + 4 * 3,
313
+ SIDEBAR_WIDTH: 375,
314
+ VIDEO_CONTROLS_MIN_WIDTH: VIDEO_CONTROLS_MIN_WIDTH$1,
315
+ ROOM_FOOTER_MIN_WIDTH: 60 * 3 + VIDEO_CONTROLS_MIN_WIDTH$1,
316
+ FLOATING_VIDEO_CONTROLS_BOTTOM_MARGIN: 20,
317
+ WATERMARK_BAR_HEIGHT: 32,
318
+ BREAKOUT_STAGE_BACKDROP_HEADER_HEIGHT: 20 + 8,
319
+ BREAKOUT_STAGE_BACKDROP_FOOTER_HEIGHT: 8 + 40 + 8,
320
+ SUBGRID_EMPTY_STAGE_MAX_WIDTH: 800,
321
+ GROUPS_CELL_MARGIN: 8,
322
+ GROUPS_CELL_PADDING: 12,
323
+ GROUPS_CELL_NAV_HEIGHT: 48 + 8,
324
+ GROUPS_CELL_AVATAR_WRAPPER_BOTTOM_MARGIN: 8,
325
+ GROUPS_CELL_AVATAR_GRID_GAP: 8,
326
+ GROUPS_CELL_MIN_WIDTH: 360,
327
+ GROUPS_CELL_MAX_WIDTH: 600,
328
+ GROUPS_ROW_HEIGHT: 72,
329
+ GROUPS_ROW_GAP: 1,
330
+ FOLDABLE_SCREEN_STAGE_PADDING: 8,
331
+ };
332
+
333
+ function makeOrigin({ top = 0, left = 0 } = {}) {
334
+ return {
335
+ top,
336
+ left,
337
+ };
338
+ }
339
+ function makeBounds({ width = 0, height = 0 } = {}) {
340
+ return {
341
+ width: Math.max(width, 0),
342
+ height: Math.max(height, 0),
343
+ };
344
+ }
345
+ function makeFrame({ top = 0, left = 0, width = 0, height = 0 } = {}) {
346
+ return {
347
+ bounds: makeBounds({ width, height }),
348
+ origin: makeOrigin({ top, left }),
349
+ };
350
+ }
351
+ function makeBox({ top = 0, left = 0, bottom = 0, right = 0 } = {}) {
352
+ return {
353
+ top,
354
+ left,
355
+ bottom,
356
+ right,
357
+ };
358
+ }
359
+ function hasBounds(bounds) {
360
+ if (!bounds) {
361
+ return false;
362
+ }
363
+ return !(bounds.width <= 0 || bounds.height <= 0);
364
+ }
365
+ function insetBounds({ bounds, fromBounds }) {
366
+ return {
367
+ width: Math.max(fromBounds.width - bounds.width, 0),
368
+ height: Math.max(fromBounds.height - bounds.height, 0),
369
+ };
370
+ }
371
+
372
+ function fitToBounds(aspectRatio, containerSize) {
373
+ const { width, height } = containerSize;
374
+ const contentHeight = height;
375
+ const contentWidth = contentHeight * aspectRatio;
376
+ const scale = Math.min(width / contentWidth, height / contentHeight);
377
+ const adjustedWidth = contentWidth * scale;
378
+ const adjustedHeight = contentHeight * scale;
379
+ return { width: adjustedWidth, height: adjustedHeight };
380
+ }
381
+ const cellContentArea = ({ width, height, rows, cols, aspectRatio, }) => {
382
+ const bounds = fitToBounds(aspectRatio, { width: width / cols, height: height / rows });
383
+ return Math.round(bounds.width * bounds.height);
384
+ };
385
+ const getWeightedSplitCount = ({ vertical, width, height, count, aspectRatio, }) => {
386
+ const choices = [1, 2, 3].map((rowCols) => cellContentArea({
387
+ width,
388
+ height,
389
+ rows: vertical ? Math.ceil(count / rowCols) : rowCols,
390
+ cols: vertical ? rowCols : Math.ceil(count / rowCols),
391
+ aspectRatio,
392
+ }));
393
+ const closest = Math.max(...choices);
394
+ const splits = choices.indexOf(closest) + 1;
395
+ return { splits, weight: closest };
396
+ };
397
+ const getGridSplits = ({ width, height, count, aspectRatio, }) => {
398
+ const verticalPick = getWeightedSplitCount({ vertical: true, width, height, count, aspectRatio });
399
+ const horizontalPick = getWeightedSplitCount({ vertical: false, width, height, count, aspectRatio });
400
+ if (verticalPick.weight > horizontalPick.weight) {
401
+ return { splits: verticalPick.splits, vertical: true };
402
+ }
403
+ return { splits: horizontalPick.splits, vertical: false };
404
+ };
405
+ function getGridSizeForCount({ count, width, height, aspectRatio, }) {
406
+ if (count <= 1) {
407
+ return {
408
+ rows: 1,
409
+ cols: 1,
410
+ };
411
+ }
412
+ const { splits, vertical } = getGridSplits({ width, height, count, aspectRatio });
413
+ if (vertical) {
414
+ return {
415
+ rows: Math.ceil(count / splits),
416
+ cols: splits,
417
+ };
418
+ }
419
+ return {
420
+ rows: splits,
421
+ cols: Math.ceil(count / splits),
422
+ };
423
+ }
424
+
425
+ function VideoCell(_a) {
426
+ var { aspectRatio, children, className, height, style, width, withRoundedCorners = false, withShadow = true } = _a, rest = __rest(_a, ["aspectRatio", "children", "className", "height", "style", "width", "withRoundedCorners", "withShadow"]);
427
+ let contentWidth = width;
428
+ let contentHeight = height;
429
+ let leftOffset = 0;
430
+ let topOffset = 0;
431
+ if (aspectRatio) {
432
+ ({ width: contentWidth, height: contentHeight } = fitToBounds(aspectRatio, {
433
+ width,
434
+ height,
435
+ }));
436
+ leftOffset = (width - contentWidth) / 2;
437
+ topOffset = (height - contentHeight) / 2;
438
+ }
439
+ const contentStyle = Object.assign({ width: `${Math.round(contentWidth)}px`, height: `${Math.round(contentHeight)}px` }, (leftOffset || topOffset
440
+ ? { transform: `translate3d(${Math.round(leftOffset)}px, ${Math.round(topOffset)}px, 0)` }
441
+ : {}));
442
+ return (React__namespace.createElement("div", { className: className, style: Object.assign({ position: "absolute", top: 0, left: 0 }, style) },
443
+ React__namespace.createElement("div", Object.assign({ style: contentStyle }, rest), React__namespace.cloneElement(children, { contentWidth, contentHeight, withRoundedCorners, withShadow }))));
444
+ }
445
+
446
+ function generateStylesFromFrame({ origin, bounds }) {
447
+ return {
448
+ top: Math.round(origin.top),
449
+ left: Math.round(origin.left),
450
+ height: Math.round(bounds.height),
451
+ width: Math.round(bounds.width),
452
+ };
453
+ }
454
+ function renderVideoCell({ cell, child, clientId, style = {}, withRoundedCorners = false, withShadow = false, }) {
455
+ const isHidden = !hasBounds(cell.bounds);
456
+ return (React__namespace.createElement(VideoCell, { width: cell.bounds.width, height: cell.bounds.height, aspectRatio: cell.aspectRatio, style: isHidden ? { width: 0, height: 0 } : style, withRoundedCorners: withRoundedCorners, withShadow: withShadow, key: clientId }, child));
457
+ }
458
+ function renderSubgridVideoCells({ content, stageLayout, withRoundedCorners, withShadow, }) {
459
+ const cells = stageLayout.subgrid.cells;
460
+ return content.map((child, index) => {
461
+ var _a, _b, _c, _d;
462
+ const cell = cells[index];
463
+ const style = { height: Math.round(cell.bounds.height), width: Math.round(cell.bounds.width), transform: "" };
464
+ const origin = {
465
+ top: stageLayout.subgrid.origin.top + cell.origin.top,
466
+ left: stageLayout.subgrid.origin.left + cell.origin.left,
467
+ };
468
+ style.transform = `translate3d(${Math.round(origin.left)}px, ${Math.round(origin.top)}px, 0)`;
469
+ const clientId = (_b = (_a = child === null || child === void 0 ? void 0 : child.props) === null || _a === void 0 ? void 0 : _a.participant) === null || _b === void 0 ? void 0 : _b.id;
470
+ const leftPaddings = ((_c = cell.paddings) === null || _c === void 0 ? void 0 : _c.left) || 0;
471
+ const rightPaddings = ((_d = cell.paddings) === null || _d === void 0 ? void 0 : _d.right) || 0;
472
+ const childWithProps = React__namespace.cloneElement(child, {
473
+ avatarSize: cell.bounds.width - leftPaddings - rightPaddings,
474
+ canZoom: false,
475
+ cellPaddings: cell.paddings,
476
+ isSmallCell: cell.isSmallCell,
477
+ isZoomedByDefault: false,
478
+ key: clientId || `subgrid-${index}`,
479
+ style,
480
+ });
481
+ return renderVideoCell({
482
+ cell,
483
+ child: childWithProps,
484
+ clientId: clientId || `subgrid-${index}`,
485
+ style,
486
+ withRoundedCorners,
487
+ withShadow,
488
+ });
489
+ });
490
+ }
491
+ function renderPresentationGridVideoCells({ content, isConstrained, stageLayout, withRoundedCorners, withShadow, }) {
492
+ var _a;
493
+ const cells = ((_a = stageLayout.presentationGrid) === null || _a === void 0 ? void 0 : _a.cells) || [];
494
+ return content.map((child, index) => {
495
+ var _a, _b;
496
+ if (!stageLayout.presentationGrid) {
497
+ return null;
498
+ }
499
+ const cell = cells[index];
500
+ const origin = {
501
+ top: stageLayout.presentationGrid.origin.top + stageLayout.presentationGrid.paddings.top + cell.origin.top,
502
+ left: stageLayout.presentationGrid.origin.left +
503
+ stageLayout.presentationGrid.paddings.left +
504
+ cell.origin.left,
505
+ };
506
+ const style = {
507
+ width: Math.round(cell.bounds.width),
508
+ height: Math.round(cell.bounds.height),
509
+ transform: `translate3d(${Math.round(origin.left)}px, ${Math.round(origin.top)}px, 0)`,
510
+ };
511
+ const clientId = (_b = (_a = child === null || child === void 0 ? void 0 : child.props) === null || _a === void 0 ? void 0 : _a.participant) === null || _b === void 0 ? void 0 : _b.id;
512
+ const childWithProps = React__namespace.cloneElement(child, {
513
+ isSmallCell: cell.isSmallCell,
514
+ isZoomedByDefault: !!isConstrained && !(child === null || child === void 0 ? void 0 : child.props.participant.isPresentation),
515
+ canZoom: !!isConstrained,
516
+ key: clientId || `presentation-${index}`,
517
+ });
518
+ return renderVideoCell({
519
+ cell,
520
+ child: childWithProps,
521
+ clientId: clientId || `presentation-${index}`,
522
+ style,
523
+ withRoundedCorners,
524
+ withShadow,
525
+ });
526
+ });
527
+ }
528
+ function renderGridVideoCells({ content, isConstrained, stageLayout, withRoundedCorners, withShadow, }) {
529
+ var _a;
530
+ const cells = ((_a = stageLayout.videoGrid) === null || _a === void 0 ? void 0 : _a.cells) || [];
531
+ const gridVideoCells = content.map((child, index) => {
532
+ var _a, _b;
533
+ if (!stageLayout.videoGrid) {
534
+ return null;
535
+ }
536
+ const cell = cells[index];
537
+ const origin = {
538
+ top: stageLayout.videoGrid.origin.top + stageLayout.videoGrid.paddings.top + cell.origin.top,
539
+ left: stageLayout.videoGrid.origin.left + stageLayout.videoGrid.paddings.left + cell.origin.left,
540
+ };
541
+ const style = {
542
+ width: Math.round(cell.bounds.width),
543
+ height: Math.round(cell.bounds.height),
544
+ transform: `translate3d(${Math.round(origin.left)}px, ${Math.round(origin.top)}px, 0)`,
545
+ };
546
+ const clientId = (_b = (_a = child === null || child === void 0 ? void 0 : child.props) === null || _a === void 0 ? void 0 : _a.participant) === null || _b === void 0 ? void 0 : _b.id;
547
+ const childWithProps = React__namespace.cloneElement(child, {
548
+ isSmallCell: cell.isSmallCell,
549
+ isZoomedByDefault: !!isConstrained && !(child === null || child === void 0 ? void 0 : child.props.participant.isPresentation),
550
+ canZoom: !!isConstrained,
551
+ key: clientId || `video-${index}`,
552
+ });
553
+ return renderVideoCell({
554
+ cell,
555
+ child: childWithProps,
556
+ clientId: clientId || `video-${index}`,
557
+ withRoundedCorners,
558
+ style,
559
+ withShadow,
560
+ });
561
+ });
562
+ return gridVideoCells;
563
+ }
564
+ function renderFloatingVideoCell({ content, containerFrame, stageLayout, withRoundedCorners, }) {
565
+ var _a;
566
+ if (!stageLayout.floatingContent) {
567
+ return null;
568
+ }
569
+ const cell = stageLayout.floatingContent;
570
+ const origin = { top: cell.origin.top, left: cell.origin.left };
571
+ const style = {
572
+ width: Math.round(cell.bounds.width),
573
+ height: Math.round(cell.bounds.height),
574
+ transform: `translate3d(${Math.round(containerFrame.origin.left + origin.left)}px, ${Math.round(containerFrame.origin.top + origin.top)}px, 0)`,
575
+ position: "fixed",
576
+ top: cell.bounds.height / 2,
577
+ left: 0,
578
+ zIndex: 40,
579
+ };
580
+ const clientId = ((_a = content.props.client) === null || _a === void 0 ? void 0 : _a.id) || "floating";
581
+ const childWithProps = React__namespace.cloneElement(content, {
582
+ isSmallCell: cell.isSmallCell,
583
+ isZoomedByDefault: false,
584
+ canZoom: false,
585
+ key: clientId,
586
+ isDraggable: false,
587
+ });
588
+ return renderVideoCell({
589
+ cell,
590
+ child: childWithProps,
591
+ clientId,
592
+ style,
593
+ withRoundedCorners,
594
+ withShadow: false,
595
+ });
596
+ }
597
+ function VideoStageLayout({ containerFrame, debug = false, featureRoundedCornersOff = false, floatingContent, gridContent = [], isConstrained = false, layoutOverflowBackdropFrame = makeFrame(), layoutVideoStage: stageLayout, presentationGridContent = [], subgridContent = [], }) {
598
+ const withRoundedCorners = !featureRoundedCornersOff && !isConstrained;
599
+ const cells = [];
600
+ if (gridContent.length) {
601
+ cells.push(...renderGridVideoCells({
602
+ content: gridContent,
603
+ isConstrained,
604
+ stageLayout,
605
+ withRoundedCorners,
606
+ withShadow: !isConstrained,
607
+ }));
608
+ }
609
+ if (presentationGridContent.length) {
610
+ cells.push(...renderPresentationGridVideoCells({
611
+ content: presentationGridContent,
612
+ isConstrained,
613
+ stageLayout,
614
+ withRoundedCorners,
615
+ withShadow: !isConstrained,
616
+ }));
617
+ }
618
+ if (floatingContent) {
619
+ cells.push(renderFloatingVideoCell({
620
+ content: floatingContent,
621
+ containerFrame,
622
+ stageLayout,
623
+ withRoundedCorners: !featureRoundedCornersOff,
624
+ }));
625
+ }
626
+ if (subgridContent.length) {
627
+ cells.push(...renderSubgridVideoCells({
628
+ content: subgridContent,
629
+ stageLayout,
630
+ withRoundedCorners: !featureRoundedCornersOff,
631
+ withShadow: true,
632
+ }));
633
+ }
634
+ return (React__namespace.createElement("div", { key: "video-stage-layout", style: {
635
+ width: "100%",
636
+ height: "100%",
637
+ position: "relative",
638
+ } },
639
+ hasBounds(layoutOverflowBackdropFrame.bounds) && (React__namespace.createElement("div", { style: generateStylesFromFrame(layoutOverflowBackdropFrame) })),
640
+ cells,
641
+ debug && (React__namespace.createElement(React__namespace.Fragment, null))));
642
+ }
643
+
644
+ const WIDE_AR = 16 / 9;
645
+ const NORMAL_AR = 4 / 3;
646
+ const clamp = ({ value, min, max }) => Math.min(Math.max(value, min), max);
647
+ function hasDuplicates(...array) {
648
+ return new Set(array).size !== array.length;
649
+ }
650
+ function findMostCommon(arr) {
651
+ return arr.sort((a, b) => arr.filter((v) => v === a).length - arr.filter((v) => v === b).length).pop();
652
+ }
653
+ function pickCellAspectRatio({ choices = [] }) {
654
+ const minAr = Math.min(...choices);
655
+ const maxAr = Math.max(...choices);
656
+ let chosenAr = null;
657
+ if (minAr === maxAr) {
658
+ chosenAr = minAr;
659
+ }
660
+ else {
661
+ const dominantAr = hasDuplicates(choices) ? findMostCommon(choices) : maxAr;
662
+ chosenAr = clamp({ value: dominantAr || maxAr, min: NORMAL_AR, max: WIDE_AR });
663
+ }
664
+ return {
665
+ minAr,
666
+ maxAr,
667
+ chosenAr,
668
+ };
669
+ }
670
+ function getCenterPadding$1({ rows, cols, cellWidth, index, cellCount, gridGap, }) {
671
+ const max = rows * cols;
672
+ const leftOver = max - cellCount;
673
+ if (!leftOver) {
674
+ return 0;
675
+ }
676
+ const lastIndex = max - leftOver - 1;
677
+ const firstIndex = lastIndex - (cols - leftOver) + 1;
678
+ const lastRowPadding = (leftOver * cellWidth) / 2 + gridGap;
679
+ return index >= firstIndex && index <= lastIndex ? lastRowPadding : 0;
680
+ }
681
+ function getCellBounds({ width, height, rows, cols, gridGap, aspectRatio, }) {
682
+ const cellWidth = (width - (cols - 1) * gridGap) / cols;
683
+ const cellHeight = (height - (rows - 1) * gridGap) / rows;
684
+ const ar = cellWidth / cellHeight;
685
+ let horizontalCorrection = 0;
686
+ let verticalCorrection = 0;
687
+ if (aspectRatio < ar) {
688
+ horizontalCorrection = cellWidth - cellHeight * aspectRatio;
689
+ }
690
+ else if (aspectRatio > ar) {
691
+ verticalCorrection = cellHeight - cellWidth / aspectRatio;
692
+ }
693
+ const totalHorizontalCorrection = horizontalCorrection * cols;
694
+ const totalVerticalCorrection = verticalCorrection * rows;
695
+ return {
696
+ cellWidth: cellWidth - horizontalCorrection,
697
+ cellHeight: cellHeight - verticalCorrection,
698
+ extraHorizontalPadding: totalHorizontalCorrection / 2,
699
+ extraVerticalPadding: totalVerticalCorrection / 2,
700
+ };
701
+ }
702
+ function calculateLayout$3({ width, height, cellCount, gridGap, cellAspectRatios = [NORMAL_AR], paddings = makeBox(), }) {
703
+ if (!cellCount) {
704
+ return {
705
+ cellCount,
706
+ cellHeight: 0,
707
+ cellWidth: 0,
708
+ cols: 0,
709
+ rows: 0,
710
+ extraHorizontalPadding: 0,
711
+ extraVerticalPadding: 0,
712
+ gridGap,
713
+ paddings,
714
+ };
715
+ }
716
+ const contentWidth = width - (paddings.left + paddings.right);
717
+ const contentHeight = height - (paddings.top + paddings.bottom);
718
+ const cellAspectRatioTuple = pickCellAspectRatio({
719
+ choices: cellAspectRatios,
720
+ });
721
+ let cellAspectRatio = cellAspectRatioTuple.chosenAr;
722
+ const { rows, cols } = getGridSizeForCount({
723
+ count: cellCount,
724
+ width: contentWidth,
725
+ height: contentHeight,
726
+ aspectRatio: cellAspectRatio,
727
+ });
728
+ if (rows === 1) {
729
+ cellAspectRatio = clamp({
730
+ value: contentWidth / cols / contentHeight,
731
+ min: Math.min(cellAspectRatioTuple.chosenAr, cellAspectRatioTuple.maxAr),
732
+ max: Math.max(cellAspectRatioTuple.chosenAr, cellAspectRatioTuple.maxAr),
733
+ });
734
+ }
735
+ else if (cols === 1) {
736
+ cellAspectRatio = clamp({
737
+ value: contentWidth / (contentHeight / rows),
738
+ min: Math.min(cellAspectRatioTuple.chosenAr, cellAspectRatioTuple.maxAr),
739
+ max: Math.max(cellAspectRatioTuple.chosenAr, cellAspectRatioTuple.maxAr),
740
+ });
741
+ }
742
+ const { cellWidth, cellHeight, extraHorizontalPadding, extraVerticalPadding } = getCellBounds({
743
+ width: contentWidth,
744
+ height: contentHeight,
745
+ rows,
746
+ cols,
747
+ gridGap,
748
+ aspectRatio: cellAspectRatio,
749
+ });
750
+ return {
751
+ cellCount,
752
+ cellHeight,
753
+ cellWidth,
754
+ cols,
755
+ rows,
756
+ extraHorizontalPadding,
757
+ extraVerticalPadding,
758
+ gridGap,
759
+ paddings,
760
+ };
761
+ }
762
+ function getCellPropsAtIndexForLayout$2({ index, layout, }) {
763
+ const { cellWidth, cellHeight, rows, cols, cellCount, gridGap } = layout;
764
+ const top = Math.floor(index / cols);
765
+ const left = Math.floor(index % cols);
766
+ const leftPadding = getCenterPadding$1({ rows, cols, cellWidth, index, cellCount, gridGap });
767
+ return {
768
+ top: top * cellHeight + top * gridGap,
769
+ left: left * cellWidth + left * gridGap + leftPadding,
770
+ width: cellWidth,
771
+ height: cellHeight,
772
+ };
773
+ }
774
+
775
+ const ROWS = "rows";
776
+ const COLUMNS = "columns";
777
+ function getPortraitLayoutGridForCount(count) {
778
+ return ({
779
+ 1: { type: ROWS, lines: [1] },
780
+ 2: { type: ROWS, lines: [1, 1] },
781
+ 3: { type: ROWS, lines: [2, 1] },
782
+ 4: { type: ROWS, lines: [2, 2] },
783
+ 5: { type: ROWS, lines: [2, 2, 1] },
784
+ 6: { type: ROWS, lines: [2, 2, 2] },
785
+ 7: { type: ROWS, lines: [3, 2, 2] },
786
+ 8: { type: ROWS, lines: [2, 2, 2, 2] },
787
+ 9: { type: ROWS, lines: [3, 2, 2, 2] },
788
+ 10: { type: ROWS, lines: [3, 3, 2, 2] },
789
+ 11: { type: ROWS, lines: [3, 3, 3, 2] },
790
+ 12: { type: ROWS, lines: [3, 3, 3, 3] },
791
+ 13: { type: ROWS, lines: [2, 2, 3, 3, 3] },
792
+ 14: { type: ROWS, lines: [2, 3, 3, 3, 3] },
793
+ 15: { type: ROWS, lines: [3, 3, 3, 3, 3] },
794
+ 16: { type: ROWS, lines: [2, 2, 3, 3, 3, 3] },
795
+ 17: { type: ROWS, lines: [2, 3, 3, 3, 3, 3] },
796
+ 18: { type: ROWS, lines: [3, 3, 3, 3, 3, 3] },
797
+ 19: { type: ROWS, lines: [2, 2, 3, 3, 3, 3, 3] },
798
+ 20: { type: ROWS, lines: [2, 3, 3, 3, 3, 3, 3] },
799
+ 21: { type: ROWS, lines: [3, 3, 3, 3, 3, 3, 3] },
800
+ 22: { type: ROWS, lines: [2, 2, 3, 3, 3, 3, 3, 3] },
801
+ 23: { type: ROWS, lines: [2, 3, 3, 3, 3, 3, 3, 3] },
802
+ 24: { type: ROWS, lines: [3, 3, 3, 3, 3, 3, 3, 3] },
803
+ }[count] || { type: ROWS, lines: [3, 3, 3, 3, 3, 3, 3, 3] });
804
+ }
805
+ function getLandscapeLayoutGridForCount(count) {
806
+ return ({
807
+ 1: { type: COLUMNS, lines: [1] },
808
+ 2: { type: COLUMNS, lines: [1, 1] },
809
+ 3: { type: COLUMNS, lines: [2, 1] },
810
+ 4: { type: COLUMNS, lines: [2, 2] },
811
+ 5: { type: ROWS, lines: [3, 2], fractions: [0.45, 0.55] },
812
+ 6: { type: COLUMNS, lines: [2, 2, 2] },
813
+ 7: { type: ROWS, lines: [4, 3], fractions: [0.45, 0.55] },
814
+ 8: { type: COLUMNS, lines: [2, 2, 2, 2] },
815
+ 9: { type: COLUMNS, lines: [3, 2, 2, 2] },
816
+ 10: { type: COLUMNS, lines: [3, 3, 2, 2] },
817
+ 11: { type: COLUMNS, lines: [3, 3, 3, 2] },
818
+ 12: { type: COLUMNS, lines: [3, 3, 3, 3] },
819
+ 13: { type: COLUMNS, lines: [4, 3, 3, 3] },
820
+ 14: { type: COLUMNS, lines: [4, 4, 3, 3] },
821
+ 15: { type: COLUMNS, lines: [3, 3, 3, 3, 3] },
822
+ 16: { type: COLUMNS, lines: [4, 3, 3, 3, 3] },
823
+ 17: { type: COLUMNS, lines: [4, 4, 3, 3, 3] },
824
+ 18: { type: COLUMNS, lines: [4, 4, 4, 3, 3] },
825
+ 19: { type: COLUMNS, lines: [4, 4, 4, 4, 3] },
826
+ 20: { type: COLUMNS, lines: [4, 4, 4, 4, 4] },
827
+ 21: { type: COLUMNS, lines: [5, 4, 4, 4, 4] },
828
+ 22: { type: COLUMNS, lines: [5, 5, 4, 4, 4] },
829
+ 23: { type: COLUMNS, lines: [5, 5, 5, 4, 4] },
830
+ 24: { type: COLUMNS, lines: [5, 5, 5, 5, 4] },
831
+ }[count] || { type: COLUMNS, lines: [5, 5, 5, 5, 5] });
832
+ }
833
+ function calcCellDimensions({ layoutGrid, dim, secDim, index, count, gridGap, }) {
834
+ const adjustedDim = dim - gridGap * (count - 1);
835
+ const cellDim = adjustedDim * (layoutGrid.fractions ? layoutGrid.fractions[index] : 1 / count);
836
+ const secCount = layoutGrid.lines[index];
837
+ const adjustedSecDim = secDim - gridGap * (secCount - 1);
838
+ const secCellDim = adjustedSecDim / secCount;
839
+ return [cellDim, secCellDim];
840
+ }
841
+ function partitionRectBy({ isHorizontal, layoutGrid, width, height, gridGap, }) {
842
+ const cellRects = [];
843
+ const origin = { x: 0, y: 0 };
844
+ const cellOrigin = { x: 0, y: 0 };
845
+ const count = layoutGrid.lines.length;
846
+ const dim = isHorizontal ? height : width;
847
+ const secDim = isHorizontal ? width : height;
848
+ for (let index = 0; index < count; index++) {
849
+ const cellDims = calcCellDimensions({ layoutGrid, dim, secDim, index, count, gridGap });
850
+ const width = isHorizontal ? cellDims[1] : cellDims[0];
851
+ const height = isHorizontal ? cellDims[0] : cellDims[1];
852
+ const cellBounds = { width, height };
853
+ const linesCount = layoutGrid.lines[index];
854
+ for (let lineIndex = 0; lineIndex < linesCount; lineIndex++) {
855
+ cellRects.push({ origin: Object.assign({}, cellOrigin), bounds: cellBounds });
856
+ if (isHorizontal) {
857
+ cellOrigin.x += cellBounds.width + gridGap;
858
+ }
859
+ else {
860
+ cellOrigin.y += cellBounds.height + gridGap;
861
+ }
862
+ }
863
+ if (isHorizontal) {
864
+ cellOrigin.y += cellBounds.height + gridGap;
865
+ cellOrigin.x = origin.x;
866
+ }
867
+ else {
868
+ cellOrigin.x += cellBounds.width + gridGap;
869
+ cellOrigin.y = origin.y;
870
+ }
871
+ }
872
+ return cellRects;
873
+ }
874
+ function partitionRectByRows({ layoutGrid, width, height, gridGap, }) {
875
+ return partitionRectBy({ isHorizontal: true, layoutGrid, width, height, gridGap });
876
+ }
877
+ function partitionRectByColumns({ layoutGrid, width, height, gridGap, }) {
878
+ return partitionRectBy({ isHorizontal: false, layoutGrid, width, height, gridGap });
879
+ }
880
+ function partitionRect({ layoutGrid, width, height, gridGap, }) {
881
+ switch (layoutGrid.type) {
882
+ case ROWS:
883
+ return partitionRectByRows({ layoutGrid, width, height, gridGap });
884
+ case COLUMNS:
885
+ return partitionRectByColumns({ layoutGrid, width, height, gridGap });
886
+ }
887
+ }
888
+ function calculateLayout$2({ width, height, cellCount, gridGap = 0, paddings = makeBox(), }) {
889
+ const contentWidth = width - (paddings.left + paddings.right);
890
+ const contentHeight = height - (paddings.top + paddings.bottom);
891
+ const isPortrait = Math.round((contentWidth / contentHeight) * 10) / 10 <= 1.0;
892
+ const layoutGrid = isPortrait
893
+ ? getPortraitLayoutGridForCount(cellCount)
894
+ : getLandscapeLayoutGridForCount(cellCount);
895
+ const cellRects = partitionRect({ layoutGrid, width: contentWidth, height: contentHeight, gridGap }) || [];
896
+ return {
897
+ cellRects,
898
+ cellCount,
899
+ gridGap,
900
+ extraHorizontalPadding: 0,
901
+ extraVerticalPadding: 0,
902
+ paddings,
903
+ };
904
+ }
905
+ function getCellPropsAtIndexForLayout$1({ index, layout }) {
906
+ const { cellRects } = layout;
907
+ const frame = cellRects[index] || makeFrame();
908
+ return {
909
+ top: frame.origin.y,
910
+ left: frame.origin.x,
911
+ width: frame.bounds.width,
912
+ height: frame.bounds.height,
913
+ };
914
+ }
915
+
916
+ function getCenterPadding({ index, isPortrait, rows, cols, cellBounds, cellCount }) {
917
+ const canFit = rows * cols;
918
+ const leftOver = canFit - cellCount;
919
+ if (!leftOver) {
920
+ return 0;
921
+ }
922
+ const lastIndex = canFit - leftOver - 1;
923
+ const dimension = isPortrait ? cols : rows;
924
+ const firstIndex = lastIndex - (dimension - leftOver) + 1;
925
+ if (index < firstIndex || index > lastIndex) {
926
+ return 0;
927
+ }
928
+ return (leftOver * (isPortrait ? cellBounds.width : cellBounds.height)) / 2;
929
+ }
930
+ function calculateLayout$1({ containerBounds, isPortrait, cellCount, cellBounds, paddings = makeBox(), }) {
931
+ let width = 0;
932
+ let height = 0;
933
+ let rows = 0;
934
+ let cols = 0;
935
+ let extraHorizontalPadding = 0;
936
+ let extraVerticalPadding = 0;
937
+ const contentWidth = containerBounds.width - paddings.left - paddings.right;
938
+ const contentHeight = containerBounds.height - paddings.top - paddings.bottom;
939
+ if (cellCount) {
940
+ if (isPortrait) {
941
+ cols = Math.floor(contentWidth / cellBounds.width);
942
+ rows = Math.ceil(cellCount / cols);
943
+ width = contentWidth;
944
+ height = rows * cellBounds.height;
945
+ extraHorizontalPadding = (contentWidth - cols * cellBounds.width) / 2;
946
+ }
947
+ else {
948
+ rows = Math.floor(contentHeight / cellBounds.height);
949
+ cols = Math.ceil(cellCount / rows);
950
+ width = cols * cellBounds.width;
951
+ height = contentHeight;
952
+ extraVerticalPadding = (contentHeight - rows * cellBounds.height) / 2;
953
+ }
954
+ }
955
+ return {
956
+ isPortrait,
957
+ width: width + paddings.left + paddings.right,
958
+ height: height + paddings.top + paddings.bottom,
959
+ cellBounds,
960
+ cellCount,
961
+ rows,
962
+ cols,
963
+ extraHorizontalPadding,
964
+ extraVerticalPadding,
965
+ paddings,
966
+ };
967
+ }
968
+ function getCellPropsAtIndexForLayout({ index, layout }) {
969
+ const { isPortrait, rows, cols, cellCount, cellBounds, extraHorizontalPadding, extraVerticalPadding, paddings } = layout;
970
+ const top = isPortrait ? Math.floor(index / cols) : Math.floor(index % rows);
971
+ const left = isPortrait ? Math.floor(index % cols) : Math.floor(index / rows);
972
+ const centerPadding = getCenterPadding({ index, isPortrait, rows, cols, cellBounds, cellCount });
973
+ return {
974
+ top: paddings.top + extraVerticalPadding + top * cellBounds.height + (isPortrait ? 0 : centerPadding),
975
+ left: paddings.left + extraHorizontalPadding + left * cellBounds.width + (isPortrait ? centerPadding : 0),
976
+ width: cellBounds.width,
977
+ height: cellBounds.height,
978
+ };
979
+ }
980
+
981
+ const { BOTTOM_TOOLBAR_HEIGHT, VIDEO_CONTROLS_MIN_WIDTH, TABLET_BREAKPOINT, SUBGRID_EMPTY_STAGE_MAX_WIDTH } = layoutConstants;
982
+ const MIN_GRID_HEIGHT = 200;
983
+ const MIN_GRID_WIDTH = 300;
984
+ const FLOATING_VIDEO_SIZE = 200;
985
+ const OVERFLOW_ROOM_SUBGRID_TOP_PADDING = 20;
986
+ const CONSTRAINED_OVERFLOW_TRIGGER = 12;
987
+ const SUBGRID_CELL_PADDING_BOX = makeBox({ top: 4, bottom: 4 + 20 + 4, left: 8, right: 8 });
988
+ function getSubgridSizeOptions({ hasOverflow, hasStage }) {
989
+ if (hasOverflow && hasStage) {
990
+ return [80];
991
+ }
992
+ return [80, 60];
993
+ }
994
+ function getMinGridBounds({ cellCount }) {
995
+ const isSmallGrid = cellCount <= 6;
996
+ const minGridHeight = isSmallGrid ? MIN_GRID_HEIGHT - 50 : MIN_GRID_HEIGHT;
997
+ const minGridWidth = isSmallGrid ? MIN_GRID_WIDTH - 50 : MIN_GRID_WIDTH;
998
+ return makeBounds({ width: minGridWidth, height: minGridHeight });
999
+ }
1000
+ function fitSupersizedContent({ bounds, aspectRatio, minGridContainerBounds, hasPresentationGrid, }) {
1001
+ const { width, height } = bounds;
1002
+ const hasVideoGrid = minGridContainerBounds.width > 0;
1003
+ if (!hasVideoGrid) {
1004
+ return {
1005
+ isPortrait: width <= height,
1006
+ supersizedContentBounds: bounds,
1007
+ };
1008
+ }
1009
+ const minHorizontalSupersizedContentWidth = Math.round(width / 2);
1010
+ const minVerticalSupersizedContentHeight = Math.round(height / 2);
1011
+ const maxHorizontalSupersizedContentWidth = Math.max(width - minGridContainerBounds.width, 0);
1012
+ const maxVerticalSupersizedContentHeight = Math.max(height - minGridContainerBounds.height, 0);
1013
+ let isPortrait = maxHorizontalSupersizedContentWidth <= maxVerticalSupersizedContentHeight;
1014
+ let horizontalCorrection = 0;
1015
+ let verticalCorrection = 0;
1016
+ if (aspectRatio) {
1017
+ const horizontalContentBounds = fitToBounds(aspectRatio, {
1018
+ width: maxHorizontalSupersizedContentWidth,
1019
+ height,
1020
+ });
1021
+ const verticalContentBounds = fitToBounds(aspectRatio, {
1022
+ width,
1023
+ height: maxVerticalSupersizedContentHeight,
1024
+ });
1025
+ const isPortraitContent = aspectRatio <= 1.0;
1026
+ isPortrait = isPortraitContent
1027
+ ? verticalContentBounds.height > horizontalContentBounds.height
1028
+ : verticalContentBounds.width > horizontalContentBounds.width;
1029
+ if (isPortrait) {
1030
+ const wastedSpace = maxVerticalSupersizedContentHeight -
1031
+ Math.max(verticalContentBounds.height, minVerticalSupersizedContentHeight);
1032
+ verticalCorrection = Math.max(wastedSpace, 0);
1033
+ }
1034
+ else {
1035
+ const wastedSpace = maxHorizontalSupersizedContentWidth -
1036
+ Math.max(horizontalContentBounds.width, minHorizontalSupersizedContentWidth);
1037
+ horizontalCorrection = Math.max(wastedSpace, 0);
1038
+ }
1039
+ }
1040
+ else if (hasPresentationGrid) {
1041
+ isPortrait = maxHorizontalSupersizedContentWidth / maxVerticalSupersizedContentHeight >= 5;
1042
+ }
1043
+ const supersizedContentBounds = {
1044
+ width: isPortrait ? width : maxHorizontalSupersizedContentWidth - horizontalCorrection,
1045
+ height: isPortrait ? maxVerticalSupersizedContentHeight - verticalCorrection : height,
1046
+ };
1047
+ return {
1048
+ isPortrait,
1049
+ supersizedContentBounds,
1050
+ };
1051
+ }
1052
+ function findBestSubgridLayout({ containerBounds, isPortrait, cellCount, cellSizeOptions, cellPaddings, paddings, }) {
1053
+ let layout;
1054
+ for (let i = 0; i < cellSizeOptions.length; i++) {
1055
+ const cellSize = cellSizeOptions[i];
1056
+ layout = calculateLayout$1({
1057
+ containerBounds,
1058
+ isPortrait,
1059
+ cellCount,
1060
+ cellBounds: makeBounds({
1061
+ width: cellSize + cellPaddings.left + cellPaddings.right,
1062
+ height: cellSize + cellPaddings.top + cellPaddings.bottom,
1063
+ }),
1064
+ paddings,
1065
+ });
1066
+ if (layout.width <= containerBounds.width && layout.height <= containerBounds.height) {
1067
+ return layout;
1068
+ }
1069
+ }
1070
+ return layout;
1071
+ }
1072
+ function calculateSubgridLayout({ containerBounds, isPortrait, cellCount, cellSizeOptions, cellPaddings, paddings, }) {
1073
+ const layout = findBestSubgridLayout({
1074
+ containerBounds,
1075
+ isPortrait,
1076
+ cellCount,
1077
+ cellSizeOptions,
1078
+ cellPaddings,
1079
+ paddings,
1080
+ });
1081
+ const isSmallCell = layout.cellBounds.width - cellPaddings.left - cellPaddings.right ===
1082
+ cellSizeOptions[cellSizeOptions.length - 1];
1083
+ const bounds = makeBounds({
1084
+ width: Math.min(layout.width, containerBounds.width),
1085
+ height: Math.min(layout.height, containerBounds.height),
1086
+ });
1087
+ const contentBounds = makeBounds({ width: layout.width, height: layout.height });
1088
+ if (cellCount === 0) {
1089
+ return {
1090
+ bounds,
1091
+ contentBounds,
1092
+ videoCells: [],
1093
+ };
1094
+ }
1095
+ return {
1096
+ bounds,
1097
+ contentBounds,
1098
+ videoCells: [...Array(cellCount)].map((_, index) => {
1099
+ const { top, left, width, height } = getCellPropsAtIndexForLayout({ index, layout });
1100
+ return {
1101
+ origin: makeOrigin({ top, left }),
1102
+ bounds: makeBounds({ width, height }),
1103
+ paddings: cellPaddings,
1104
+ isSmallCell,
1105
+ };
1106
+ }),
1107
+ };
1108
+ }
1109
+ function calculateStageLayout({ cellPaddings, cellSizeOptions, containerBounds, containerOrigin, gridGap, hasConstrainedOverflow, hasPresentationContent, hasVideoContent, isConstrained, isMaximizeMode, isPortrait, isSmallScreen, maxGridWidth, shouldOverflowSubgrid, subgridVideos, }) {
1110
+ const { width, height } = containerBounds;
1111
+ const hasVideos = hasPresentationContent || hasVideoContent;
1112
+ const hasSubgrid = subgridVideos.length > 0;
1113
+ if (!hasSubgrid && !hasVideos) {
1114
+ return {
1115
+ isPortrait,
1116
+ videosContainer: makeFrame(),
1117
+ subgrid: Object.assign(Object.assign({}, makeFrame()), { contentBounds: makeBounds(), cells: [] }),
1118
+ hasOverflow: false,
1119
+ };
1120
+ }
1121
+ if (!hasSubgrid) {
1122
+ return {
1123
+ isPortrait,
1124
+ videosContainer: makeFrame(Object.assign(Object.assign({}, containerBounds), containerOrigin)),
1125
+ subgrid: Object.assign(Object.assign({}, makeFrame()), { contentBounds: makeBounds(), cells: [] }),
1126
+ hasOverflow: hasConstrainedOverflow,
1127
+ };
1128
+ }
1129
+ const topSubgridPadding = shouldOverflowSubgrid ? OVERFLOW_ROOM_SUBGRID_TOP_PADDING : 0;
1130
+ let _containerBounds = null;
1131
+ if (!hasVideos) {
1132
+ _containerBounds = makeBounds({
1133
+ width: Math.min(SUBGRID_EMPTY_STAGE_MAX_WIDTH, containerBounds.width),
1134
+ height: containerBounds.height,
1135
+ });
1136
+ }
1137
+ else if (shouldOverflowSubgrid) {
1138
+ const cellSize = cellSizeOptions[0];
1139
+ let visibleSubgridHeight = isMaximizeMode
1140
+ ? BOTTOM_TOOLBAR_HEIGHT
1141
+ : BOTTOM_TOOLBAR_HEIGHT +
1142
+ cellPaddings.top +
1143
+ (isConstrained || isSmallScreen ? cellSize / 2 : cellSize) +
1144
+ cellPaddings.bottom;
1145
+ if (containerBounds.height - visibleSubgridHeight < MIN_GRID_HEIGHT) {
1146
+ visibleSubgridHeight = 0;
1147
+ }
1148
+ _containerBounds = makeBounds({
1149
+ width: containerBounds.width,
1150
+ height: visibleSubgridHeight,
1151
+ });
1152
+ }
1153
+ else {
1154
+ if (isMaximizeMode) {
1155
+ _containerBounds = makeBounds({
1156
+ width: containerBounds.width,
1157
+ height: cellSizeOptions[0] + cellPaddings.top + cellPaddings.bottom,
1158
+ });
1159
+ }
1160
+ else {
1161
+ _containerBounds = isPortrait
1162
+ ? makeBounds({ width: containerBounds.width, height: containerBounds.height / 2 })
1163
+ : makeBounds({ width: containerBounds.width / 2, height: containerBounds.height });
1164
+ }
1165
+ }
1166
+ let leftSubgridPadding = 0;
1167
+ if (_containerBounds.width > maxGridWidth) {
1168
+ leftSubgridPadding = (_containerBounds.width - maxGridWidth) / 2;
1169
+ }
1170
+ const subgridLayout = calculateSubgridLayout({
1171
+ containerBounds: _containerBounds,
1172
+ isPortrait,
1173
+ cellCount: subgridVideos.length,
1174
+ cellSizeOptions,
1175
+ cellPaddings,
1176
+ paddings: makeBox({ top: topSubgridPadding, left: leftSubgridPadding, right: leftSubgridPadding }),
1177
+ });
1178
+ const overflowNeedBounds = makeBounds({
1179
+ width: subgridLayout.contentBounds.width - _containerBounds.width,
1180
+ height: subgridLayout.contentBounds.height - _containerBounds.height,
1181
+ });
1182
+ const hasOverflow = overflowNeedBounds.width > 0 || overflowNeedBounds.height > 0;
1183
+ let subgridBounds = null;
1184
+ if (!hasVideos || shouldOverflowSubgrid || hasOverflow) {
1185
+ subgridBounds = subgridLayout.contentBounds;
1186
+ }
1187
+ else {
1188
+ subgridBounds = subgridLayout.bounds;
1189
+ }
1190
+ let videosContainerBounds;
1191
+ let _subgridContainerShareBounds = subgridBounds;
1192
+ if (hasVideos) {
1193
+ if (shouldOverflowSubgrid) {
1194
+ _subgridContainerShareBounds = makeBounds({
1195
+ width: 0,
1196
+ height: _containerBounds.height + topSubgridPadding + gridGap,
1197
+ });
1198
+ }
1199
+ else {
1200
+ _subgridContainerShareBounds = isPortrait
1201
+ ? makeBounds({ width: 0, height: subgridLayout.bounds.height + gridGap })
1202
+ : makeBounds({ width: subgridLayout.bounds.width + gridGap, height: 0 });
1203
+ }
1204
+ videosContainerBounds = insetBounds({
1205
+ bounds: _subgridContainerShareBounds,
1206
+ fromBounds: containerBounds,
1207
+ });
1208
+ }
1209
+ else {
1210
+ videosContainerBounds = makeBounds();
1211
+ }
1212
+ const videosContainerOrigin = Object.assign({}, containerOrigin);
1213
+ let subgridOrigin;
1214
+ if (hasVideos) {
1215
+ subgridOrigin = makeOrigin({
1216
+ top: isPortrait
1217
+ ? Math.max(containerOrigin.top + height - _subgridContainerShareBounds.height + gridGap, 0)
1218
+ : containerOrigin.top,
1219
+ left: isPortrait ? containerOrigin.left : Math.max(containerOrigin.left + width - subgridBounds.width, 0),
1220
+ });
1221
+ }
1222
+ else {
1223
+ subgridOrigin = makeOrigin({
1224
+ top: containerOrigin.top + Math.max(0, (height - subgridBounds.height) / 2),
1225
+ left: containerOrigin.left + Math.max(0, (width - subgridBounds.width) / 2),
1226
+ });
1227
+ }
1228
+ return {
1229
+ isPortrait,
1230
+ videosContainer: makeFrame(Object.assign(Object.assign({}, videosContainerBounds), videosContainerOrigin)),
1231
+ subgrid: Object.assign(Object.assign({}, makeFrame(Object.assign(Object.assign({}, subgridBounds), subgridOrigin))), { cells: subgridLayout.videoCells.map((cell, index) => {
1232
+ var _a, _b;
1233
+ return (Object.assign({ aspectRatio: ((_a = subgridVideos[index]) === null || _a === void 0 ? void 0 : _a.aspectRatio) || 1, type: "video", clientId: (_b = subgridVideos[index]) === null || _b === void 0 ? void 0 : _b.clientId }, cell));
1234
+ }), contentBounds: subgridLayout.contentBounds }),
1235
+ hasOverflow: hasOverflow || shouldOverflowSubgrid || hasConstrainedOverflow,
1236
+ overflowNeedBounds: shouldOverflowSubgrid ? makeBounds() : overflowNeedBounds,
1237
+ };
1238
+ }
1239
+ function calculateVideosContainerLayout({ containerBounds, containerOrigin, gridGap, supersizedContentAspectRatio, hasPresentationContent, hasPresentationGrid, hasVideoContent, minGridBounds, }) {
1240
+ const { width, height } = containerBounds;
1241
+ let isPortrait = width <= height;
1242
+ let presentationGridBounds = makeBounds();
1243
+ let presentationGridOrigin = makeOrigin();
1244
+ let videoGridBounds = hasVideoContent ? Object.assign({}, containerBounds) : makeBounds();
1245
+ let videoGridOrigin = hasVideoContent ? Object.assign({}, containerOrigin) : makeOrigin();
1246
+ if (hasPresentationContent) {
1247
+ const minGridContainerBounds = makeBounds({
1248
+ width: hasVideoContent ? minGridBounds.width + gridGap : 0,
1249
+ height: hasVideoContent ? minGridBounds.height + gridGap : 0,
1250
+ });
1251
+ const supersizedContentLayout = fitSupersizedContent({
1252
+ bounds: containerBounds,
1253
+ aspectRatio: supersizedContentAspectRatio || 1,
1254
+ minGridContainerBounds,
1255
+ hasPresentationGrid,
1256
+ });
1257
+ isPortrait = supersizedContentLayout.isPortrait;
1258
+ presentationGridBounds = supersizedContentLayout.supersizedContentBounds;
1259
+ presentationGridOrigin = Object.assign({}, containerOrigin);
1260
+ if (hasVideoContent) {
1261
+ videoGridBounds = makeBounds({
1262
+ width: isPortrait
1263
+ ? containerBounds.width
1264
+ : containerBounds.width - presentationGridBounds.width - gridGap,
1265
+ height: isPortrait
1266
+ ? containerBounds.height - presentationGridBounds.height - gridGap
1267
+ : containerBounds.height,
1268
+ });
1269
+ videoGridOrigin = makeOrigin({
1270
+ top: isPortrait ? containerOrigin.top + presentationGridBounds.height + gridGap : containerOrigin.top,
1271
+ left: isPortrait ? containerOrigin.left : containerOrigin.left + presentationGridBounds.width + gridGap,
1272
+ });
1273
+ }
1274
+ }
1275
+ return {
1276
+ isPortrait,
1277
+ presentationGrid: Object.assign({}, makeFrame(Object.assign(Object.assign({}, presentationGridBounds), presentationGridOrigin))),
1278
+ videoGrid: makeFrame(Object.assign(Object.assign({}, videoGridBounds), videoGridOrigin)),
1279
+ };
1280
+ }
1281
+ function calculateGridLayout({ containerBounds, paddings = makeBox(), videos, isConstrained, maxGridWidth, gridGap, }) {
1282
+ const { width, height } = containerBounds;
1283
+ const cappedWidth = maxGridWidth ? Math.min(width, maxGridWidth) : width;
1284
+ const cellCount = videos.length;
1285
+ let gridLayout = null;
1286
+ let videoCells = null;
1287
+ if (isConstrained) {
1288
+ const constrainedGridLayout = calculateLayout$2({
1289
+ width,
1290
+ height,
1291
+ cellCount,
1292
+ gridGap,
1293
+ paddings,
1294
+ });
1295
+ const isSmallCell = cellCount > 4;
1296
+ videoCells = videos.map((video, index) => {
1297
+ const cellProps = getCellPropsAtIndexForLayout$1({ index, layout: constrainedGridLayout });
1298
+ return {
1299
+ clientId: video.clientId,
1300
+ isDraggable: video.isDraggable,
1301
+ origin: makeOrigin({
1302
+ top: cellProps.top,
1303
+ left: cellProps.left,
1304
+ }),
1305
+ bounds: makeBounds({
1306
+ width: cellProps.width,
1307
+ height: cellProps.height,
1308
+ }),
1309
+ aspectRatio: cellProps.width / cellProps.height,
1310
+ isSmallCell,
1311
+ type: "video",
1312
+ };
1313
+ });
1314
+ gridLayout = constrainedGridLayout;
1315
+ }
1316
+ else {
1317
+ const cellAspectRatios = videos.map((video) => video.aspectRatio || 1);
1318
+ const minGridBounds = getMinGridBounds({ cellCount });
1319
+ const centerGridLayout = calculateLayout$3({
1320
+ width: cappedWidth,
1321
+ height,
1322
+ cellCount,
1323
+ gridGap,
1324
+ cellAspectRatios,
1325
+ paddings,
1326
+ });
1327
+ videoCells = videos.map((video, index) => {
1328
+ const cellProps = getCellPropsAtIndexForLayout$2({ index, layout: centerGridLayout });
1329
+ const isSmallCell = centerGridLayout.cellWidth < minGridBounds.width;
1330
+ const shouldZoom = isConstrained || isSmallCell;
1331
+ const aspectRatio = shouldZoom
1332
+ ? centerGridLayout.cellWidth / centerGridLayout.cellHeight
1333
+ : video.aspectRatio || 1;
1334
+ return {
1335
+ clientId: video.clientId,
1336
+ isDraggable: video.isDraggable,
1337
+ origin: makeOrigin({
1338
+ top: cellProps.top,
1339
+ left: cellProps.left,
1340
+ }),
1341
+ bounds: makeBounds({
1342
+ width: cellProps.width,
1343
+ height: cellProps.height,
1344
+ }),
1345
+ aspectRatio,
1346
+ isSmallCell,
1347
+ type: "video",
1348
+ };
1349
+ });
1350
+ gridLayout = centerGridLayout;
1351
+ }
1352
+ return {
1353
+ videoCells,
1354
+ extraHorizontalPadding: width !== cappedWidth
1355
+ ? gridLayout.extraHorizontalPadding + (width - cappedWidth) / 2
1356
+ : gridLayout.extraHorizontalPadding,
1357
+ extraVerticalPadding: gridLayout.extraVerticalPadding,
1358
+ paddings: gridLayout.paddings,
1359
+ gridGap,
1360
+ };
1361
+ }
1362
+ function calculateFloatingLayout({ roomBounds, containerFrame, floatingVideo, videoControlsHeight, margin = 8, }) {
1363
+ if (!floatingVideo) {
1364
+ return null;
1365
+ }
1366
+ const bounds = fitToBounds(floatingVideo.aspectRatio || 0, {
1367
+ width: FLOATING_VIDEO_SIZE,
1368
+ height: FLOATING_VIDEO_SIZE,
1369
+ });
1370
+ const isFloating = !(roomBounds.height - containerFrame.bounds.height - containerFrame.origin.top);
1371
+ const isConstrained = containerFrame.bounds.width - (bounds.width + margin) * 2 < VIDEO_CONTROLS_MIN_WIDTH;
1372
+ let verticalOffset = 0;
1373
+ if (isFloating && isConstrained) {
1374
+ verticalOffset = videoControlsHeight * -1;
1375
+ }
1376
+ else if (!isFloating && !isConstrained) {
1377
+ verticalOffset = videoControlsHeight;
1378
+ }
1379
+ const origin = makeOrigin({
1380
+ top: containerFrame.origin.top + (containerFrame.bounds.height - bounds.height - margin) + verticalOffset,
1381
+ left: containerFrame.origin.left + (containerFrame.bounds.width - bounds.width - margin),
1382
+ });
1383
+ const videoCell = {
1384
+ clientId: floatingVideo.clientId,
1385
+ isDraggable: floatingVideo.isDraggable,
1386
+ origin,
1387
+ bounds,
1388
+ aspectRatio: floatingVideo.aspectRatio,
1389
+ isSmallCell: true,
1390
+ };
1391
+ return videoCell;
1392
+ }
1393
+ function rebalanceLayoutPaddedAreas({ a, b, gridGap, isPortrait, }) {
1394
+ const aPad = isPortrait ? a.vertical : a.horizontal;
1395
+ const bPad = isPortrait ? b.vertical : b.horizontal;
1396
+ if (aPad === bPad) {
1397
+ return { a: 0, b: 0 };
1398
+ }
1399
+ const sArea = aPad < bPad ? a : b;
1400
+ const sAreaPad = isPortrait ? sArea.vertical : sArea.horizontal;
1401
+ const spaceBetween = gridGap + (aPad + bPad);
1402
+ const offset = (spaceBetween + sAreaPad) / 2 - sAreaPad;
1403
+ return {
1404
+ a: sArea === a ? offset : 0,
1405
+ b: sArea === b ? offset : 0,
1406
+ };
1407
+ }
1408
+ function rebalanceLayoutInPlace({ videosContainerLayout, gridLayout, presentationGridLayout, stageLayout, gridGap, shouldOverflowSubgrid, }) {
1409
+ const hasPresentationGrid = videosContainerLayout.presentationGrid.bounds.width > 0;
1410
+ const hasVideoGrid = videosContainerLayout.videoGrid.bounds.width > 0;
1411
+ const hasSubgrid = stageLayout.subgrid.bounds.width > 0;
1412
+ const videoGridRebalanceOffset = { vertical: 0, horizontal: 0 };
1413
+ if (hasPresentationGrid && hasVideoGrid) {
1414
+ const correction = rebalanceLayoutPaddedAreas({
1415
+ a: {
1416
+ horizontal: presentationGridLayout.extraHorizontalPadding,
1417
+ vertical: presentationGridLayout.extraVerticalPadding,
1418
+ },
1419
+ b: {
1420
+ horizontal: gridLayout.extraHorizontalPadding,
1421
+ vertical: gridLayout.extraVerticalPadding,
1422
+ },
1423
+ gridGap,
1424
+ isPortrait: videosContainerLayout.isPortrait,
1425
+ });
1426
+ if (videosContainerLayout.isPortrait) {
1427
+ videosContainerLayout.presentationGrid.origin.top += correction.a;
1428
+ videosContainerLayout.videoGrid.origin.top -= correction.b;
1429
+ videoGridRebalanceOffset.vertical = correction.b;
1430
+ }
1431
+ else {
1432
+ videosContainerLayout.presentationGrid.origin.left += correction.a;
1433
+ videosContainerLayout.videoGrid.origin.left -= correction.b;
1434
+ videoGridRebalanceOffset.horizontal = correction.b;
1435
+ }
1436
+ }
1437
+ if (hasSubgrid && (hasPresentationGrid || hasVideoGrid) && !shouldOverflowSubgrid) {
1438
+ const presentationGridArea = {
1439
+ horizontal: presentationGridLayout.extraHorizontalPadding,
1440
+ vertical: presentationGridLayout.extraVerticalPadding,
1441
+ };
1442
+ const gridArea = {
1443
+ horizontal: gridLayout.extraHorizontalPadding + videoGridRebalanceOffset.horizontal,
1444
+ vertical: gridLayout.extraVerticalPadding + videoGridRebalanceOffset.vertical,
1445
+ };
1446
+ let area;
1447
+ const hasBothGrids = hasPresentationGrid && hasVideoGrid;
1448
+ if (hasBothGrids && videosContainerLayout.isPortrait && !stageLayout.isPortrait) {
1449
+ area = presentationGridArea.horizontal < gridArea.horizontal ? presentationGridArea : gridArea;
1450
+ }
1451
+ else if (hasBothGrids && !videosContainerLayout.isPortrait && stageLayout.isPortrait) {
1452
+ area = presentationGridArea.vertical < gridArea.vertical ? presentationGridArea : gridArea;
1453
+ }
1454
+ else if (hasVideoGrid) {
1455
+ area = gridArea;
1456
+ }
1457
+ else {
1458
+ area = presentationGridArea;
1459
+ }
1460
+ const correction = rebalanceLayoutPaddedAreas({
1461
+ a: area,
1462
+ b: {
1463
+ horizontal: 0,
1464
+ vertical: 0,
1465
+ },
1466
+ gridGap,
1467
+ isPortrait: stageLayout.isPortrait,
1468
+ });
1469
+ if (stageLayout.isPortrait) {
1470
+ stageLayout.subgrid.origin.top -= correction.b;
1471
+ }
1472
+ else {
1473
+ stageLayout.subgrid.origin.left -= correction.b;
1474
+ }
1475
+ }
1476
+ }
1477
+ function redistributeEmptySpaceToSubgridInPlace({ vPaddings, hPaddings, stageLayout, videosContainerLayout, }) {
1478
+ if (stageLayout.isPortrait && (vPaddings.presentationGrid || vPaddings.grid)) {
1479
+ const totalVPadding = videosContainerLayout.isPortrait
1480
+ ? vPaddings.presentationGrid * 2 + vPaddings.grid * 2
1481
+ : vPaddings.presentationGrid + vPaddings.grid;
1482
+ stageLayout.videosContainer.bounds.height -= totalVPadding;
1483
+ stageLayout.subgrid.origin.top -= totalVPadding;
1484
+ stageLayout.subgrid.bounds.height += totalVPadding;
1485
+ videosContainerLayout.presentationGrid.bounds.height -= vPaddings.presentationGrid * 2;
1486
+ videosContainerLayout.videoGrid.bounds.height -= vPaddings.grid * 2;
1487
+ if (videosContainerLayout.isPortrait) {
1488
+ videosContainerLayout.videoGrid.origin.top -= vPaddings.presentationGrid;
1489
+ }
1490
+ }
1491
+ if (!stageLayout.isPortrait && (hPaddings.presentationGrid || hPaddings.grid)) {
1492
+ const totalHPadding = videosContainerLayout.isPortrait
1493
+ ? hPaddings.presentationGrid + hPaddings.grid
1494
+ : hPaddings.presentationGrid * 2 + hPaddings.grid * 2;
1495
+ stageLayout.videosContainer.bounds.width -= totalHPadding;
1496
+ stageLayout.subgrid.origin.left -= totalHPadding;
1497
+ stageLayout.subgrid.bounds.width += totalHPadding;
1498
+ videosContainerLayout.presentationGrid.bounds.width -= hPaddings.presentationGrid * 2;
1499
+ videosContainerLayout.videoGrid.bounds.width -= hPaddings.grid * 2;
1500
+ if (!videosContainerLayout.isPortrait) {
1501
+ videosContainerLayout.videoGrid.origin.left -= hPaddings.grid;
1502
+ }
1503
+ }
1504
+ }
1505
+ function findGridsEmptySpaceToRedistribute({ need, stageLayout, gridLayout, presentationGridLayout, videosContainerLayout, }) {
1506
+ let vPaddings = { presentationGrid: 0, grid: 0 };
1507
+ let hPaddings = { presentationGrid: 0, grid: 0 };
1508
+ if (stageLayout.isPortrait) {
1509
+ const minDim = Math.min(gridLayout.extraVerticalPadding, presentationGridLayout.extraVerticalPadding);
1510
+ vPaddings = {
1511
+ presentationGrid: Math.min(videosContainerLayout.isPortrait ? presentationGridLayout.extraVerticalPadding : minDim, need.height),
1512
+ grid: Math.min(videosContainerLayout.isPortrait ? gridLayout.extraVerticalPadding : minDim, need.height),
1513
+ };
1514
+ }
1515
+ else {
1516
+ const minDim = Math.min(gridLayout.extraHorizontalPadding, presentationGridLayout.extraHorizontalPadding);
1517
+ hPaddings = {
1518
+ presentationGrid: Math.min(videosContainerLayout.isPortrait ? minDim : presentationGridLayout.extraHorizontalPadding, need.width),
1519
+ grid: Math.min(videosContainerLayout.isPortrait ? minDim : gridLayout.extraHorizontalPadding, need.width),
1520
+ };
1521
+ }
1522
+ return { vPaddings, hPaddings };
1523
+ }
1524
+ function calculateGridLayouts({ gridGap, isConstrained, presentationVideos, videos, videosContainerLayout, gridLayoutPaddings = makeBox(), presentationGridLayoutPaddings = makeBox(), maxGridWidth, }) {
1525
+ const gridLayout = calculateGridLayout({
1526
+ containerBounds: videosContainerLayout.videoGrid.bounds,
1527
+ gridGap,
1528
+ isConstrained,
1529
+ maxGridWidth,
1530
+ paddings: gridLayoutPaddings,
1531
+ videos,
1532
+ });
1533
+ const presentationGridLayout = calculateGridLayout({
1534
+ containerBounds: videosContainerLayout.presentationGrid.bounds,
1535
+ gridGap,
1536
+ isConstrained,
1537
+ maxGridWidth,
1538
+ paddings: presentationGridLayoutPaddings,
1539
+ videos: presentationVideos,
1540
+ });
1541
+ return { gridLayout, presentationGridLayout };
1542
+ }
1543
+ function calculateLayout({ floatingVideo, frame, gridGap, isConstrained, isMaximizeMode = false, isXLMeetingSize = false, paddings = makeBox(), presentationVideos = [], rebalanceLayout = false, roomBounds, roomLayoutHasOverlow = false, subgridCellPaddings = SUBGRID_CELL_PADDING_BOX, subgridVideos = [], videoControlsHeight = 0, videos = [], videoGridGap, }) {
1544
+ let shouldOverflowSubgrid = isXLMeetingSize && (screen ? screen.width <= 2048 : true);
1545
+ const hasPresentationContent = !!presentationVideos.length;
1546
+ const hasPresentationGrid = presentationVideos.length > 1;
1547
+ const supersizedContentAspectRatio = hasPresentationContent && !hasPresentationGrid ? presentationVideos[0].aspectRatio : null;
1548
+ const hasVideoContent = !!videos.length;
1549
+ const width = frame.bounds.width - paddings.left - paddings.right;
1550
+ let height = frame.bounds.height - paddings.top - paddings.bottom;
1551
+ const maxGridWidth = Math.max(25 * 88, (80 / 100) * width);
1552
+ const hasConstrainedOverflow = isConstrained && videos.length > CONSTRAINED_OVERFLOW_TRIGGER;
1553
+ const lineHeight = height / 4;
1554
+ const extraLines = Math.ceil((videos.length - CONSTRAINED_OVERFLOW_TRIGGER) / 3);
1555
+ height = hasConstrainedOverflow ? height + lineHeight * extraLines : height;
1556
+ const stageBounds = makeBounds({ width, height });
1557
+ const stageOrigin = makeOrigin({ top: paddings.top, left: paddings.left });
1558
+ const _minBounds = getMinGridBounds({ cellCount: videos.length });
1559
+ const minGridBounds = _minBounds;
1560
+ const isSmallScreen = roomBounds.width < TABLET_BREAKPOINT || roomBounds.height < TABLET_BREAKPOINT;
1561
+ const forceStageLayoutPortrait = isMaximizeMode;
1562
+ const stageLayoutIsPortrait = forceStageLayoutPortrait ||
1563
+ shouldOverflowSubgrid ||
1564
+ !(hasPresentationContent || hasVideoContent) ||
1565
+ stageBounds.width <= stageBounds.height;
1566
+ const stableStageLayoutProps = {
1567
+ cellPaddings: subgridCellPaddings,
1568
+ containerBounds: stageBounds,
1569
+ containerOrigin: stageOrigin,
1570
+ gridGap,
1571
+ hasPresentationContent,
1572
+ hasVideoContent,
1573
+ isConstrained,
1574
+ isMaximizeMode,
1575
+ isSmallScreen,
1576
+ subgridVideos,
1577
+ maxGridWidth,
1578
+ };
1579
+ let stageLayout = calculateStageLayout(Object.assign(Object.assign({}, stableStageLayoutProps), { cellSizeOptions: getSubgridSizeOptions({
1580
+ hasOverflow: shouldOverflowSubgrid,
1581
+ hasStage: hasPresentationContent || hasVideoContent,
1582
+ }), isPortrait: stageLayoutIsPortrait, shouldOverflowSubgrid,
1583
+ hasConstrainedOverflow }));
1584
+ let forceRerunAsOverflow = false;
1585
+ if (!shouldOverflowSubgrid && roomLayoutHasOverlow && !stageLayout.hasOverflow) {
1586
+ const _stageLayout = calculateStageLayout(Object.assign(Object.assign({}, stableStageLayoutProps), { containerBounds: makeBounds({
1587
+ width: stageBounds.width,
1588
+ height: stageBounds.height - BOTTOM_TOOLBAR_HEIGHT,
1589
+ }), cellSizeOptions: getSubgridSizeOptions({
1590
+ hasOverflow: shouldOverflowSubgrid,
1591
+ hasStage: hasPresentationContent || hasVideoContent,
1592
+ }), isPortrait: stageLayoutIsPortrait, shouldOverflowSubgrid,
1593
+ hasConstrainedOverflow }));
1594
+ if (_stageLayout.hasOverflow) {
1595
+ forceRerunAsOverflow = true;
1596
+ }
1597
+ }
1598
+ if (forceRerunAsOverflow || (stageLayout.hasOverflow && !shouldOverflowSubgrid)) {
1599
+ shouldOverflowSubgrid = true;
1600
+ stageLayout = calculateStageLayout(Object.assign(Object.assign({}, stableStageLayoutProps), { cellSizeOptions: getSubgridSizeOptions({
1601
+ hasOverflow: true,
1602
+ hasStage: hasPresentationContent || hasVideoContent,
1603
+ }), isPortrait: true, shouldOverflowSubgrid,
1604
+ hasConstrainedOverflow }));
1605
+ }
1606
+ const videosContainerLayout = calculateVideosContainerLayout({
1607
+ containerBounds: stageLayout.videosContainer.bounds,
1608
+ containerOrigin: stageLayout.videosContainer.origin,
1609
+ gridGap,
1610
+ supersizedContentAspectRatio: supersizedContentAspectRatio || 1,
1611
+ hasPresentationContent,
1612
+ hasPresentationGrid,
1613
+ hasVideoContent,
1614
+ minGridBounds,
1615
+ });
1616
+ let { gridLayout, presentationGridLayout } = calculateGridLayouts({
1617
+ gridGap: videoGridGap || gridGap,
1618
+ isConstrained,
1619
+ presentationVideos,
1620
+ videos,
1621
+ videosContainerLayout,
1622
+ maxGridWidth,
1623
+ });
1624
+ if (stageLayout.hasOverflow && !shouldOverflowSubgrid) {
1625
+ const { vPaddings, hPaddings } = findGridsEmptySpaceToRedistribute({
1626
+ need: stageLayout.overflowNeedBounds || makeBounds(),
1627
+ stageLayout,
1628
+ gridLayout,
1629
+ presentationGridLayout,
1630
+ videosContainerLayout,
1631
+ });
1632
+ if (vPaddings.presentationGrid || vPaddings.grid || hPaddings.presentationGrid || hPaddings.grid) {
1633
+ redistributeEmptySpaceToSubgridInPlace({ vPaddings, hPaddings, stageLayout, videosContainerLayout });
1634
+ ({ gridLayout, presentationGridLayout } = calculateGridLayouts({
1635
+ gridGap: videoGridGap || gridGap,
1636
+ isConstrained,
1637
+ presentationVideos,
1638
+ videos,
1639
+ videosContainerLayout,
1640
+ maxGridWidth,
1641
+ }));
1642
+ }
1643
+ }
1644
+ const floatingLayout = calculateFloatingLayout({
1645
+ roomBounds,
1646
+ containerFrame: frame,
1647
+ floatingVideo: floatingVideo || null,
1648
+ videoControlsHeight,
1649
+ });
1650
+ if (rebalanceLayout) {
1651
+ rebalanceLayoutInPlace({
1652
+ videosContainerLayout,
1653
+ gridLayout,
1654
+ presentationGridLayout,
1655
+ stageLayout,
1656
+ gridGap,
1657
+ shouldOverflowSubgrid,
1658
+ });
1659
+ }
1660
+ let overflowBoundsHeight = paddings.top + stageLayout.videosContainer.bounds.height + stageLayout.subgrid.bounds.height + paddings.bottom;
1661
+ if (hasPresentationContent || hasVideoContent) {
1662
+ overflowBoundsHeight += gridGap;
1663
+ }
1664
+ return {
1665
+ isPortrait: stageLayout.isPortrait,
1666
+ hasOverflow: stageLayout.hasOverflow,
1667
+ videosContainer: Object.assign(Object.assign({}, videosContainerLayout), { bounds: makeBounds({
1668
+ width: stageLayout.videosContainer.bounds.width,
1669
+ height: stageLayout.videosContainer.bounds.height,
1670
+ }), origin: makeOrigin({
1671
+ top: stageLayout.videosContainer.origin.top,
1672
+ left: stageLayout.videosContainer.origin.left,
1673
+ }) }),
1674
+ bounds: makeBounds({
1675
+ height: shouldOverflowSubgrid ? overflowBoundsHeight : frame.bounds.height,
1676
+ width: frame.bounds.width,
1677
+ }),
1678
+ gridGap,
1679
+ presentationGrid: Object.assign(Object.assign({}, videosContainerLayout.presentationGrid), { cells: presentationGridLayout.videoCells, paddings: makeBox({
1680
+ top: presentationGridLayout.paddings.top + presentationGridLayout.extraVerticalPadding,
1681
+ bottom: presentationGridLayout.paddings.bottom + presentationGridLayout.extraVerticalPadding,
1682
+ left: presentationGridLayout.paddings.left + presentationGridLayout.extraHorizontalPadding,
1683
+ right: presentationGridLayout.paddings.right + presentationGridLayout.extraHorizontalPadding,
1684
+ }) }),
1685
+ videoGrid: Object.assign(Object.assign({}, videosContainerLayout.videoGrid), { cells: gridLayout.videoCells, paddings: makeBox({
1686
+ top: gridLayout.paddings.top + gridLayout.extraVerticalPadding,
1687
+ bottom: gridLayout.paddings.bottom + gridLayout.extraVerticalPadding,
1688
+ left: gridLayout.paddings.left + gridLayout.extraHorizontalPadding,
1689
+ right: gridLayout.paddings.right + gridLayout.extraHorizontalPadding,
1690
+ }) }),
1691
+ subgrid: stageLayout.subgrid,
1692
+ floatingContent: Object.assign(Object.assign(Object.assign({}, floatingLayout), floatingVideo), { origin: (floatingLayout === null || floatingLayout === void 0 ? void 0 : floatingLayout.origin) || makeOrigin(), bounds: (floatingLayout === null || floatingLayout === void 0 ? void 0 : floatingLayout.bounds) || makeBounds(), aspectRatio: (floatingVideo === null || floatingVideo === void 0 ? void 0 : floatingVideo.aspectRatio) || 1 }),
1693
+ };
1694
+ }
1695
+
1696
+ function makeVideoCellView({ aspectRatio, avatarSize, cellPaddings, client = undefined, isDraggable = true, isPlaceholder = false, isSubgrid = false, }) {
1697
+ return {
1698
+ aspectRatio: aspectRatio || 16 / 9,
1699
+ avatarSize,
1700
+ cellPaddings,
1701
+ client,
1702
+ clientId: (client === null || client === void 0 ? void 0 : client.id) || "",
1703
+ isDraggable,
1704
+ isPlaceholder,
1705
+ isSubgrid,
1706
+ type: "video",
1707
+ };
1708
+ }
1709
+
1710
+ const STAGE_PARTICIPANT_LIMIT = 12;
1711
+ const ACTIVE_VIDEO_SUBGRID_TRIGGER = 12;
1712
+ const ACTIVE_VIDEOS_PHONE_LIMIT = 24;
1713
+
1714
+ function calculateSubgridViews({ clientViews, activeVideosSubgridTrigger, shouldShowSubgrid, spotlightedParticipants, maximizedParticipant, isPhoneResolution, }) {
1715
+ if (!shouldShowSubgrid) {
1716
+ return [];
1717
+ }
1718
+ const hasSpotlights = spotlightedParticipants.length > 0;
1719
+ const hasPresentationStage = hasSpotlights;
1720
+ const notMaximized = maximizedParticipant
1721
+ ? clientViews.filter((client) => client.id !== maximizedParticipant.id)
1722
+ : clientViews;
1723
+ const notSpotlighted = notMaximized.filter((client) => !client.isPresentation && !spotlightedParticipants.includes(client));
1724
+ const noVideoViews = notSpotlighted.filter((client) => !client.isVideoEnabled);
1725
+ const videoLimitReached = notSpotlighted.filter((client) => client.isVideoEnabled).length > activeVideosSubgridTrigger;
1726
+ const unmutedVideos = notSpotlighted.filter((client) => !noVideoViews.includes(client) && client.isAudioEnabled);
1727
+ const mutedVideos = notSpotlighted.filter((client) => !noVideoViews.includes(client) && !client.isAudioEnabled);
1728
+ if (noVideoViews.length && hasPresentationStage) {
1729
+ return [...mutedVideos, ...noVideoViews];
1730
+ }
1731
+ if (isPhoneResolution && notSpotlighted.length > ACTIVE_VIDEOS_PHONE_LIMIT) {
1732
+ const sorted = [...unmutedVideos, ...mutedVideos];
1733
+ const inGrid = sorted.slice(0, ACTIVE_VIDEOS_PHONE_LIMIT);
1734
+ if (inGrid.length <= ACTIVE_VIDEOS_PHONE_LIMIT) {
1735
+ return [...sorted.filter((client) => !inGrid.includes(client)), ...noVideoViews];
1736
+ }
1737
+ else {
1738
+ return [...mutedVideos, ...noVideoViews];
1739
+ }
1740
+ }
1741
+ if (videoLimitReached && mutedVideos.length) {
1742
+ const sorted = [...unmutedVideos, ...mutedVideos];
1743
+ const inGrid = sorted.slice(0, activeVideosSubgridTrigger);
1744
+ if (inGrid.length <= activeVideosSubgridTrigger) {
1745
+ return [...mutedVideos.filter((client) => !inGrid.includes(client)), ...noVideoViews];
1746
+ }
1747
+ else {
1748
+ return [...mutedVideos, ...noVideoViews];
1749
+ }
1750
+ }
1751
+ return noVideoViews;
1752
+ }
1753
+ function useGridParticipants({ activeVideosSubgridTrigger = ACTIVE_VIDEO_SUBGRID_TRIGGER, stageParticipantLimit = STAGE_PARTICIPANT_LIMIT, forceSubgrid = true, enableSubgrid = true, maximizedParticipant, floatingParticipant, isConstrained = false, } = {}) {
1754
+ const allClientViews = useAppSelector(core.selectAllClientViews);
1755
+ const spotlightedParticipants = useAppSelector(core.selectSpotlightedClientViews);
1756
+ const numParticipants = useAppSelector(core.selectNumParticipants);
1757
+ const floatingClientView = React__namespace.useMemo(() => {
1758
+ return floatingParticipant;
1759
+ }, [floatingParticipant]);
1760
+ const clientViewsNotFloating = React__namespace.useMemo(() => {
1761
+ if (floatingClientView) {
1762
+ return allClientViews.filter((c) => c.id !== floatingClientView.id);
1763
+ }
1764
+ return allClientViews;
1765
+ }, [allClientViews, floatingClientView]);
1766
+ const shouldShowSubgrid = React__namespace.useMemo(() => {
1767
+ if (!enableSubgrid) {
1768
+ return false;
1769
+ }
1770
+ return forceSubgrid ? true : numParticipants > stageParticipantLimit;
1771
+ }, [forceSubgrid, numParticipants, stageParticipantLimit, enableSubgrid]);
1772
+ const clientViewsInSubgrid = React__namespace.useMemo(() => {
1773
+ return calculateSubgridViews({
1774
+ clientViews: clientViewsNotFloating,
1775
+ activeVideosSubgridTrigger,
1776
+ shouldShowSubgrid,
1777
+ spotlightedParticipants,
1778
+ maximizedParticipant,
1779
+ isPhoneResolution: isConstrained,
1780
+ });
1781
+ }, [clientViewsNotFloating, shouldShowSubgrid, activeVideosSubgridTrigger, spotlightedParticipants, isConstrained]);
1782
+ const clientViewsOnStage = React__namespace.useMemo(() => {
1783
+ return clientViewsNotFloating.filter((client) => !clientViewsInSubgrid.includes(client));
1784
+ }, [clientViewsNotFloating, clientViewsInSubgrid]);
1785
+ const clientViewsInPresentationGrid = React__namespace.useMemo(() => {
1786
+ if (maximizedParticipant) {
1787
+ return [maximizedParticipant];
1788
+ }
1789
+ return spotlightedParticipants;
1790
+ }, [spotlightedParticipants, maximizedParticipant]);
1791
+ const clientViewsInGrid = React__namespace.useMemo(() => {
1792
+ return clientViewsOnStage.filter((client) => !clientViewsInPresentationGrid.includes(client));
1793
+ }, [clientViewsOnStage, clientViewsInPresentationGrid]);
1794
+ return {
1795
+ floatingClientView,
1796
+ clientViewsInGrid,
1797
+ clientViewsInPresentationGrid,
1798
+ clientViewsInSubgrid,
1799
+ };
1800
+ }
1801
+
1802
+ function useGrid({ activeVideosSubgridTrigger, forceSubgrid, stageParticipantLimit = STAGE_PARTICIPANT_LIMIT, gridGap = 8, videoGridGap = 8, enableSubgrid = true, enableConstrainedGrid = true, } = {}) {
1803
+ const [containerBounds, setContainerBounds] = React__namespace.useState({ width: 0, height: 0 });
1804
+ const [isConstrained, setIsConstrained] = React__namespace.useState(false);
1805
+ const [clientAspectRatios, setClientAspectRatios] = React__namespace.useState({});
1806
+ const [maximizedParticipant, setMaximizedParticipant] = React__namespace.useState(null);
1807
+ const [floatingParticipant, setFloatingParticipant] = React__namespace.useState(null);
1808
+ const { clientViewsInGrid, clientViewsInPresentationGrid, clientViewsInSubgrid, floatingClientView } = useGridParticipants({
1809
+ activeVideosSubgridTrigger,
1810
+ forceSubgrid,
1811
+ stageParticipantLimit,
1812
+ enableSubgrid,
1813
+ maximizedParticipant,
1814
+ floatingParticipant,
1815
+ isConstrained: !!enableConstrainedGrid && !!isConstrained,
1816
+ });
1817
+ const cellViewsFloating = React__namespace.useMemo(() => {
1818
+ return floatingClientView
1819
+ ? [
1820
+ makeVideoCellView({
1821
+ client: floatingClientView,
1822
+ aspectRatio: clientAspectRatios[floatingClientView.id],
1823
+ avatarSize: 0,
1824
+ cellPaddings: { top: 0, right: 0 },
1825
+ }),
1826
+ ]
1827
+ : [];
1828
+ }, [floatingClientView, clientAspectRatios]);
1829
+ const cellViewsVideoGrid = React__namespace.useMemo(() => {
1830
+ return clientViewsInGrid.map((client) => {
1831
+ return makeVideoCellView({
1832
+ client,
1833
+ aspectRatio: clientAspectRatios[client.id],
1834
+ avatarSize: 0,
1835
+ cellPaddings: { top: 0, right: 0 },
1836
+ });
1837
+ });
1838
+ }, [clientViewsInGrid, clientAspectRatios]);
1839
+ const cellViewsInPresentationGrid = React__namespace.useMemo(() => {
1840
+ return clientViewsInPresentationGrid.map((client) => {
1841
+ return makeVideoCellView({
1842
+ client,
1843
+ aspectRatio: clientAspectRatios[client.id],
1844
+ avatarSize: 0,
1845
+ cellPaddings: { top: 0, right: 0 },
1846
+ });
1847
+ });
1848
+ }, [clientViewsInPresentationGrid, clientAspectRatios]);
1849
+ const cellViewsInSubgrid = React__namespace.useMemo(() => {
1850
+ return clientViewsInSubgrid.map((client) => {
1851
+ return makeVideoCellView({
1852
+ client,
1853
+ aspectRatio: clientAspectRatios[client.id],
1854
+ avatarSize: 0,
1855
+ cellPaddings: { top: 0, right: 0 },
1856
+ isSubgrid: true,
1857
+ });
1858
+ });
1859
+ }, [clientViewsInSubgrid, clientAspectRatios]);
1860
+ const containerFrame = React__namespace.useMemo(() => {
1861
+ return makeFrame(containerBounds);
1862
+ }, [containerBounds]);
1863
+ React__namespace.useEffect(() => {
1864
+ if (!enableConstrainedGrid) {
1865
+ return;
1866
+ }
1867
+ setIsConstrained(containerBounds.width < 500 || containerBounds.height < 500);
1868
+ }, [containerBounds, enableConstrainedGrid]);
1869
+ const videoStage = React__namespace.useMemo(() => {
1870
+ return calculateLayout({
1871
+ floatingVideo: cellViewsFloating[0],
1872
+ frame: containerFrame,
1873
+ gridGap,
1874
+ isConstrained,
1875
+ roomBounds: containerFrame.bounds,
1876
+ videos: cellViewsVideoGrid,
1877
+ videoGridGap,
1878
+ presentationVideos: cellViewsInPresentationGrid,
1879
+ subgridVideos: cellViewsInSubgrid,
1880
+ });
1881
+ }, [
1882
+ containerFrame,
1883
+ cellViewsFloating,
1884
+ cellViewsVideoGrid,
1885
+ cellViewsInPresentationGrid,
1886
+ cellViewsInSubgrid,
1887
+ gridGap,
1888
+ videoGridGap,
1889
+ ]);
1890
+ return {
1891
+ containerFrame,
1892
+ cellViewsFloating,
1893
+ cellViewsVideoGrid,
1894
+ cellViewsInPresentationGrid,
1895
+ cellViewsInSubgrid,
1896
+ clientAspectRatios,
1897
+ videoStage,
1898
+ setContainerBounds,
1899
+ setClientAspectRatios,
1900
+ maximizedParticipant,
1901
+ setMaximizedParticipant,
1902
+ floatingParticipant,
1903
+ setFloatingParticipant,
1904
+ isConstrained,
1905
+ };
1906
+ }
1907
+
1908
+ const getInitialsFromName = (name = "") => {
1909
+ name = name.trim();
1910
+ if (name) {
1911
+ const initials = name.split(/-| /).map((n) => runes(n)[0]);
1912
+ return initials.slice(0, 3);
1913
+ }
1914
+ return [];
1915
+ };
1916
+ const Initials = ({ name }) => {
1917
+ const initials = getInitialsFromName(name);
1918
+ const fontSize = [0, 56, 48, 40][initials.length] || 32;
1919
+ const initialsStr = initials.join("").toUpperCase();
1920
+ return (React__namespace.createElement("svg", { viewBox: "-60 -60 120 120", "aria-hidden": "true", style: {
1921
+ height: "100%",
1922
+ pointerEvents: "none",
1923
+ width: "100%",
1924
+ } },
1925
+ React__namespace.createElement("text", { x: 0, y: 0, textAnchor: "middle", dominantBaseline: "central", fontSize: fontSize }, initialsStr)));
1926
+ };
1927
+ function Avatar(_a) {
1928
+ var { avatarUrl, className, size = 40, name, variant = "round" } = _a, rest = __rest(_a, ["avatarUrl", "className", "size", "name", "variant"]);
1929
+ return (React__namespace.createElement("div", Object.assign({ style: {
1930
+ height: `${size}px`,
1931
+ width: `${size}px`,
1932
+ userSelect: "none",
1933
+ overflow: "hidden",
1934
+ borderRadius: variant === "round" ? "50%" : "4px",
1935
+ backgroundColor: "#f8e3c8",
1936
+ }, className: className, title: name }, rest), !avatarUrl && name ? (React__namespace.createElement(Initials, { name: name })) : (React__namespace.createElement("img", { src: avatarUrl || "", alt: "", style: {
1937
+ height: "100%",
1938
+ width: "100%",
1939
+ objectFit: "cover",
1940
+ maxWidth: "initial",
1941
+ } }))));
1942
+ }
1943
+
1944
+ function VideoMutedIndicator({ avatarUrl, displayName, isSmallCell, withRoundedCorners }) {
1945
+ return (React__namespace.createElement("div", { style: {
1946
+ display: "flex",
1947
+ flexDirection: "column",
1948
+ alignItems: "center",
1949
+ justifyContent: "center",
1950
+ position: "absolute",
1951
+ top: 0,
1952
+ left: 0,
1953
+ height: "100%",
1954
+ width: "100%",
1955
+ borderRadius: withRoundedCorners ? "8px" : "0",
1956
+ } },
1957
+ React__namespace.createElement("div", { style: {
1958
+ height: isSmallCell ? 60 : 80,
1959
+ width: isSmallCell ? 60 : 80,
1960
+ pointerEvents: "none",
1961
+ position: "relative",
1962
+ } },
1963
+ React__namespace.createElement(Avatar, { variant: "square", avatarUrl: avatarUrl, name: displayName, size: isSmallCell ? 60 : 80 }))));
1964
+ }
1965
+
1966
+ const Popover = PopoverPrimitive__namespace.Root;
1967
+ const PopoverTrigger = PopoverPrimitive__namespace.Trigger;
1968
+ const PopoverContent = React__namespace.forwardRef((_a, ref) => {
1969
+ var { style, align = "center", sideOffset = 4 } = _a, props = __rest(_a, ["style", "align", "sideOffset"]);
1970
+ return (React__namespace.createElement(PopoverPrimitive__namespace.Portal, null,
1971
+ React__namespace.createElement(PopoverPrimitive__namespace.Content, Object.assign({ ref: ref, align: align, sideOffset: sideOffset, style: Object.assign({ width: "200px", backgroundColor: "#fff", border: "1px solid #e5e5e5", borderRadius: "0.375rem", boxShadow: "0 1px 2px 0 rgb(0 0 0 / 0.05)", color: "#333", fontSize: "14px", lineHeight: "1.5", padding: "16px", zIndex: 50, outline: "none" }, style) }, props))));
1972
+ });
1973
+ PopoverContent.displayName = PopoverPrimitive__namespace.Content.displayName;
1974
+
1975
+ const GridContext = React__namespace.createContext({});
1976
+ const GridCellContext = React__namespace.createContext({});
1977
+ const useGridCell = () => {
1978
+ const gridContext = React__namespace.useContext(GridContext);
1979
+ const gridCellContext = React__namespace.useContext(GridCellContext);
1980
+ if (!gridCellContext) {
1981
+ throw new Error("useGridCell must be used within a GridCell");
1982
+ }
1983
+ return Object.assign(Object.assign({}, gridContext), gridCellContext);
1984
+ };
1985
+
1986
+ const ParticipantMenuContext = React__namespace.createContext({});
1987
+ const useParticipantMenu = () => {
1988
+ const context = React__namespace.useContext(ParticipantMenuContext);
1989
+ const gridCellContext = useGridCell();
1990
+ if (!context) {
1991
+ throw new Error("useParticipantMenu must be used within a ParticipantMenu");
1992
+ }
1993
+ return Object.assign(Object.assign({}, context), gridCellContext);
1994
+ };
1995
+ const ParticipantMenu = (props) => {
1996
+ const { children } = props, rest = __rest(props, ["children"]);
1997
+ const [open, setOpen] = React__namespace.useState(false);
1998
+ return (React__namespace.createElement(ParticipantMenuContext.Provider, { value: { open, setOpen } },
1999
+ React__namespace.createElement(Popover, Object.assign({}, rest, { open: open, onOpenChange: setOpen }), children)));
2000
+ };
2001
+ const ParticipantMenuContent = React__namespace.forwardRef((_a, ref) => {
2002
+ var { children, style } = _a, props = __rest(_a, ["children", "style"]);
2003
+ return (React__namespace.createElement(PopoverContent, Object.assign({ ref: ref, style: Object.assign({ display: "flex", flexDirection: "column", justifyContent: "center", alignItems: "center", minWidth: "180px", maxWidth: "300px", maxHeight: "100vh", overflowY: "auto", padding: 0 }, style) }, props), children));
2004
+ });
2005
+ ParticipantMenuContent.displayName = "ParticipantMenuContent";
2006
+ const ParticipantMenuTrigger = React__namespace.forwardRef((_a, ref) => {
2007
+ var { children, style } = _a, props = __rest(_a, ["children", "style"]);
2008
+ return (React__namespace.createElement(PopoverTrigger, Object.assign({ ref: ref, style: Object.assign({ position: "absolute", top: "10px", right: "20px", textDecoration: "none", whiteSpace: "nowrap", border: "none", cursor: "pointer" }, style) }, props), children));
2009
+ });
2010
+ ParticipantMenuTrigger.displayName = PopoverTrigger.displayName;
2011
+ const ParticipantMenuItem = React__namespace.forwardRef((_a, ref) => {
2012
+ var { children, style, participantAction } = _a, props = __rest(_a, ["children", "style", "participantAction"]);
2013
+ const { participant, setOpen, maximizedParticipant, setMaximizedParticipant, setFloatingParticipant, floatingParticipant, } = useParticipantMenu();
2014
+ const dispatch = useAppDispatch();
2015
+ const spotlightedParticipants = useAppSelector(core.selectSpotlightedClientViews);
2016
+ const isSpotlighted = spotlightedParticipants.find((p) => p.id === participant.id);
2017
+ const isMaximized = (maximizedParticipant === null || maximizedParticipant === void 0 ? void 0 : maximizedParticipant.id) === participant.id;
2018
+ const isFloating = (floatingParticipant === null || floatingParticipant === void 0 ? void 0 : floatingParticipant.id) === participant.id;
2019
+ let onClick;
2020
+ switch (participantAction) {
2021
+ case "maximize":
2022
+ onClick = () => {
2023
+ if (isMaximized) {
2024
+ setMaximizedParticipant(null);
2025
+ }
2026
+ else {
2027
+ setMaximizedParticipant(participant);
2028
+ }
2029
+ setOpen(false);
2030
+ };
2031
+ break;
2032
+ case "spotlight":
2033
+ onClick = () => {
2034
+ if (isSpotlighted) {
2035
+ dispatch(core.doRemoveSpotlight({ id: participant.id }));
2036
+ }
2037
+ else {
2038
+ dispatch(core.doSpotlightParticipant({ id: participant.id }));
2039
+ }
2040
+ setOpen(false);
2041
+ };
2042
+ break;
2043
+ case "float":
2044
+ onClick = () => {
2045
+ if (isFloating) {
2046
+ setFloatingParticipant(null);
2047
+ }
2048
+ else {
2049
+ setFloatingParticipant(participant);
2050
+ }
2051
+ setOpen(false);
2052
+ };
2053
+ break;
2054
+ }
2055
+ return (React__namespace.createElement("button", Object.assign({ ref: ref, role: "menuitem", tabIndex: -1, onClick: onClick !== null && onClick !== void 0 ? onClick : props.onClick, style: Object.assign({ alignItems: "stretch", backgroundColor: "transparent", border: "none", cursor: "pointer", display: "flex", height: "40px", lineHeight: "40px", minWidth: "140px", padding: "0 12px", textAlign: "left", textDecoration: "none", whiteSpace: "nowrap", width: "100%" }, style) }, props), children));
2056
+ });
2057
+ ParticipantMenuItem.displayName = "ParticipantMenuItem";
2058
+
2059
+ function EllipsisIcon(props) {
2060
+ return (React__namespace.createElement("svg", Object.assign({ width: "100%", height: "100%", viewBox: "0 0 24 24", xmlns: "http://www.w3.org/2000/svg" }, props),
2061
+ React__namespace.createElement("g", null,
2062
+ React__namespace.createElement("circle", { cx: "6", cy: "12", r: "2" }),
2063
+ React__namespace.createElement("circle", { cx: "12", cy: "12", r: "2" }),
2064
+ React__namespace.createElement("circle", { cx: "18", cy: "12", r: "2" }))));
2065
+ }
2066
+
2067
+ function MaximizeOnIcon(props) {
2068
+ return (React__namespace.createElement("svg", Object.assign({ width: "100%", height: "100%", viewBox: "0 0 24 24", xmlns: "http://www.w3.org/2000/svg" }, props),
2069
+ React__namespace.createElement("path", { fillRule: "evenodd", clipRule: "evenodd", d: "m13.875 3c0 .62132.5037 1.125 1.125 1.125h3.284l-4.0795 4.0795c-.4393.43934-.4393 1.15166 0 1.591.4393.4393 1.1517.4393 1.591 0l4.0795-4.07951v3.28401c0 .62132.5037 1.125 1.125 1.125s1.125-.50368 1.125-1.125v-6c0-.15254-.0304-.29799-.0854-.43063-.0536-.12968-.1326-.25135-.2368-.35749l-.0147-.01469c-.203-.19928-.4812-.32219-.7881-.32219h-6c-.6213 0-1.125.50368-1.125 1.125zm-9.75 12c0-.6213-.50368-1.125-1.125-1.125s-1.125.5037-1.125 1.125v6c0 .3069.12291.5851.32219.7881.00486.005.00975.0099.01469.0147.10614.1042.22781.1832.35749.2368.13264.055.27809.0854.43063.0854h6c.62132 0 1.125-.5037 1.125-1.125s-.50368-1.125-1.125-1.125h-3.28401l4.07951-4.0795c.4393-.4393.4393-1.1517 0-1.591-.43934-.4393-1.15166-.4393-1.591 0l-4.0795 4.0795z" })));
2070
+ }
2071
+
2072
+ function SpotlightIcon(props) {
2073
+ return (React__namespace.createElement("svg", Object.assign({ width: "100%", height: "100%", viewBox: "0 0 24 24", xmlns: "http://www.w3.org/2000/svg" }, props),
2074
+ React__namespace.createElement("path", { fillRule: "evenodd", clipRule: "evenodd", d: "m15.241.146691c-.1011-.1955882-.3809-.1955879-.482 0l-1.1063 2.139639c-.0258.04991-.0665.09058-.1164.11639l-2.1396 1.10629c-.1956.10113-.1956.38085 0 .48198l2.1396 1.10629c.0499.02581.0906.06648.1164.11639l1.1063 2.13964c.1011.19559.3809.19559.482 0l1.1063-2.13964c.0258-.04991.0665-.09058.1164-.11639l2.1396-1.10629c.1956-.10113.1956-.38085 0-.48198l-2.1396-1.10629c-.0499-.02581-.0906-.06648-.1164-.11639zm-13.741 2.900259v14.67495c0 .0511.00116.1018.00345.1523-.00229.0418-.00345.0837-.00345.1258 0 .5674.21005 1.1103.59339 1.6103.24265.3472.55042.6455.90566.8773 1.61225 1.213 4.36988 2.0124 7.50095 2.0124 4.3315 0 7.9484-1.53 8.8068-3.5681.0651-.1472.1148-.3024.1455-.4658.0315-.1532.0477-.3087.0477-.4661 0-.6032-.2374-1.1788-.668-1.7045-.1377-.1975-.2995-.4017-.4877-.6127l-14.22219-13.74809c-.28845-.27884-.67396-.43471-1.07516-.43471-.85436 0-1.54695.69259-1.54695 1.54695zm19.2646 4.59631c.0988-.19101.372-.19101.4708 0l.8546 1.65303c.0253.04874.065.08846.1137.11366l1.653.85465c.1911.0988.1911.372 0 .4708l-1.653.8546c-.0487.0253-.0884.065-.1137.1137l-.8546 1.653c-.0988.1911-.372.1911-.4708 0l-.8546-1.653c-.0253-.0487-.065-.0884-.1137-.1137l-1.653-.8546c-.1911-.0988-.1911-.372 0-.4708l1.653-.85465c.0487-.0252.0884-.06492.1137-.11366zm-3.2651 10.35684c0 .0769-.1144.6851-1.5305 1.3931-1.2964.6482-3.2274 1.1069-5.4695 1.1069s-4.17308-.4587-5.46952-1.1069c-1.41607-.708-1.53047-1.3162-1.53047-1.3931 0-.077.1144-.6851 1.53047-1.3932 1.29644-.6482 3.22742-1.1068 5.46952-1.1068s4.1731.4586 5.4695 1.1068c1.4161.7081 1.5305 1.3162 1.5305 1.3932z" })));
2075
+ }
2076
+
2077
+ function PopOutIcon(props) {
2078
+ return (React__namespace.createElement("svg", Object.assign({ width: "100%", height: "100%", viewBox: "0 0 24 24", xmlns: "http://www.w3.org/2000/svg" }, props),
2079
+ React__namespace.createElement("path", { clipRule: "evenodd", fillRule: "evenodd", d: "m2.25 3v4.5c0 .41421.33579.75.75.75h4.5c.41421 0 .75-.33579.75-.75v-4.5c0-.41421-.33579-.75-.75-.75h-4.5c-.41421 0-.75.33579-.75.75zm-2.25 4.5v-4.5c0-1.65685 1.34315-3 3-3h4.5c1.65685 0 3 1.34315 3 3v4.5c0 1.65685-1.34315 3-3 3h-4.5c-1.65685 0-3-1.34315-3-3zm2.25 9v4.5c0 .4142.33579.75.75.75h4.5c.41421 0 .75-.3358.75-.75v-4.5c0-.4142-.33579-.75-.75-.75h-4.5c-.41421 0-.75.3358-.75.75zm-2.25 4.5v-4.5c0-1.6569 1.34315-3 3-3h4.5c1.65685 0 3 1.3431 3 3v4.5c0 1.6569-1.34315 3-3 3h-4.5c-1.65685 0-3-1.3431-3-3zm15.75-13.5v-4.5c0-.41421.3358-.75.75-.75h4.5c.4142 0 .75.33579.75.75v4.5c0 .41421-.3358.75-.75.75h-4.5c-.4142 0-.75-.33579-.75-.75zm-2.25-4.5v4.5c0 1.65685 1.3431 3 3 3h4.5c1.6569 0 3-1.34315 3-3v-4.5c0-1.65685-1.3431-3-3-3h-4.5c-1.6569 0-3 1.34315-3 3zm1.875 19.5c0-.6213.5037-1.125 1.125-1.125h3.284l-5.5795-5.5795c-.4393-.4393-.4393-1.1517 0-1.591s1.1517-.4393 1.591 0l5.5795 5.5795v-3.284c0-.6213.5037-1.125 1.125-1.125s1.125.5037 1.125 1.125v6c0 .308-.1238.5872-.3244.7903-.0033.0034-.0067.0068-.0101.0102-.1067.1054-.2292.1851-.3599.2391-.1326.055-.2781.0854-.4306.0854h-6c-.6213 0-1.125-.5037-1.125-1.125z" })));
2080
+ }
2081
+
2082
+ function PopInIcon(props) {
2083
+ return (React__namespace.createElement("svg", Object.assign({ width: "100%", height: "100%", viewBox: "0 0 24 24", xmlns: "http://www.w3.org/2000/svg" }, props),
2084
+ React__namespace.createElement("path", { clipRule: "evenodd", fillRule: "evenodd", d: "m2.25 3v4.5c0 .41421.33579.75.75.75h4.5c.41421 0 .75-.33579.75-.75v-4.5c0-.41421-.33579-.75-.75-.75h-4.5c-.41421 0-.75.33579-.75.75zm-2.25 4.5v-4.5c0-1.65685 1.34315-3 3-3h4.5c1.65685 0 3 1.34315 3 3v4.5c0 1.65685-1.34315 3-3 3h-4.5c-1.65685 0-3-1.34315-3-3zm2.25 9v4.5c0 .4142.33579.75.75.75h4.5c.41421 0 .75-.3358.75-.75v-4.5c0-.4142-.33579-.75-.75-.75h-4.5c-.41421 0-.75.3358-.75.75zm-2.25 4.5v-4.5c0-1.6569 1.34315-3 3-3h4.5c1.65685 0 3 1.3431 3 3v4.5c0 1.6569-1.34315 3-3 3h-4.5c-1.65685 0-3-1.3431-3-3zm15.75-13.5v-4.5c0-.41421.3358-.75.75-.75h4.5c.4142 0 .75.33579.75.75v4.5c0 .41421-.3358.75-.75.75h-4.5c-.4142 0-.75-.33579-.75-.75zm-2.25-4.5v4.5c0 1.65685 1.3431 3 3 3h4.5c1.6569 0 3-1.34315 3-3v-4.5c0-1.65685-1.3431-3-3-3h-4.5c-1.6569 0-3 1.34315-3 3zm8.625 12c0 .6213-.5037 1.125-1.125 1.125h-3.284l5.5795 5.5795c.4393.4393.4393 1.1517 0 1.591s-1.1517.4393-1.591 0l-5.5795-5.5795v3.284c0 .6213-.5037 1.125-1.125 1.125s-1.125-.5037-1.125-1.125v-6c0-.3081.1238-.5872.3244-.7904.0034-.0034.0068-.0068.0102-.0102.1067-.1053.2292-.185.3598-.239.1326-.055.2781-.0854.4306-.0854h6c.6213 0 1.125.5037 1.125 1.125z" })));
2085
+ }
2086
+
2087
+ function DefaultParticipantMenu({ participant }) {
2088
+ const spotlightedParticipants = useAppSelector(core.selectSpotlightedClientViews);
2089
+ const isSpotlighted = spotlightedParticipants.find((p) => p.id === participant.id);
2090
+ const { isHovered, maximizedParticipant, floatingParticipant } = useGridCell();
2091
+ const isMaximized = (maximizedParticipant === null || maximizedParticipant === void 0 ? void 0 : maximizedParticipant.id) === participant.id;
2092
+ const isFloating = (floatingParticipant === null || floatingParticipant === void 0 ? void 0 : floatingParticipant.id) === participant.id;
2093
+ if (!isHovered) {
2094
+ return null;
2095
+ }
2096
+ return (React__namespace.createElement(ParticipantMenu, null,
2097
+ React__namespace.createElement(ParticipantMenuTrigger, { style: {
2098
+ display: "flex",
2099
+ justifyContent: "center",
2100
+ alignItems: "center",
2101
+ backgroundColor: "#fff",
2102
+ borderRadius: "6px",
2103
+ padding: "4px",
2104
+ } },
2105
+ React__namespace.createElement(EllipsisIcon, { height: 20, width: 20, transform: "rotate(90)" })),
2106
+ React__namespace.createElement(ParticipantMenuContent, null,
2107
+ participant.isLocalClient ? (React__namespace.createElement(ParticipantMenuItem, { participantAction: "float", style: {
2108
+ display: "flex",
2109
+ alignItems: "center",
2110
+ gap: "10px",
2111
+ } }, isFloating ? (React__namespace.createElement(React__namespace.Fragment, null,
2112
+ React__namespace.createElement(PopInIcon, { height: 16, width: 16 }),
2113
+ "Move to grid")) : (React__namespace.createElement(React__namespace.Fragment, null,
2114
+ React__namespace.createElement(PopOutIcon, { height: 16, width: 16 }),
2115
+ "Pop out")))) : null,
2116
+ React__namespace.createElement(ParticipantMenuItem, { participantAction: "maximize", style: {
2117
+ display: "flex",
2118
+ alignItems: "center",
2119
+ gap: "10px",
2120
+ } },
2121
+ React__namespace.createElement(MaximizeOnIcon, { height: 16, width: 16 }),
2122
+ isMaximized ? "Minimize" : "Maximize"),
2123
+ React__namespace.createElement(ParticipantMenuItem, { participantAction: "spotlight", style: {
2124
+ display: "flex",
2125
+ alignItems: "center",
2126
+ gap: "10px",
2127
+ } },
2128
+ React__namespace.createElement(SpotlightIcon, { height: 16, width: 16 }),
2129
+ isSpotlighted ? "Remove spotlight" : "Spotlight"))));
2130
+ }
2131
+
2132
+ const GridCell = React__namespace.forwardRef(({ className, participant, children }, ref) => {
2133
+ const [isHovered, setIsHovered] = React__namespace.useState(false);
2134
+ const handleMouseEnter = React__namespace.useCallback(() => {
2135
+ setIsHovered(true);
2136
+ }, []);
2137
+ const handleMouseLeave = React__namespace.useCallback(() => {
2138
+ setIsHovered(false);
2139
+ }, []);
2140
+ return (React__namespace.createElement(GridCellContext.Provider, { value: { participant, isHovered } },
2141
+ React__namespace.createElement("div", { ref: ref, className: className, onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, style: { height: "100%", width: "100%" } }, children)));
2142
+ });
2143
+ GridCell.displayName = "GridCell";
2144
+ const GridVideoView = React__namespace.forwardRef((_a, ref) => {
2145
+ var { stream, style } = _a, rest = __rest(_a, ["stream", "style"]);
2146
+ const videoEl = React__namespace.useRef(null);
2147
+ const { onSetClientAspectRatio, clientAspectRatios, participant, isConstrained } = useGridCell();
2148
+ if (!participant)
2149
+ return null;
2150
+ const aspectRatio = clientAspectRatios[participant.id];
2151
+ React__namespace.useImperativeHandle(ref, () => {
2152
+ return videoEl.current;
2153
+ });
2154
+ const handleResize = React__namespace.useCallback(() => {
2155
+ const ar = videoEl.current && videoEl.current.captureAspectRatio();
2156
+ if (ar && ar !== aspectRatio && participant.id) {
2157
+ onSetClientAspectRatio({ aspectRatio: ar, clientId: participant.id });
2158
+ }
2159
+ }, [clientAspectRatios, participant.id, onSetClientAspectRatio]);
2160
+ const s = stream || participant.stream;
2161
+ if (!s) {
2162
+ return null;
2163
+ }
2164
+ return (React__namespace.createElement(VideoView, Object.assign({ ref: videoEl, style: Object.assign(Object.assign({ borderRadius: isConstrained ? 0 : "8px" }, (isConstrained ? { objectFit: "cover" } : {})), style) }, rest, { stream: s, onVideoResize: handleResize })));
2165
+ });
2166
+ GridVideoView.displayName = "GridVideoView";
2167
+ function renderCellView({ cellView, enableParticipantMenu, render }) {
2168
+ const participant = cellView === null || cellView === void 0 ? void 0 : cellView.client;
2169
+ if (!participant) {
2170
+ return undefined;
2171
+ }
2172
+ switch (cellView.type) {
2173
+ case "video":
2174
+ return (React__namespace.createElement(GridCell, { participant: participant }, participant.isVideoEnabled ? (React__namespace.createElement(React__namespace.Fragment, null, render ? (render({ participant })) : (React__namespace.createElement(React__namespace.Fragment, null,
2175
+ React__namespace.createElement(GridVideoView, null),
2176
+ enableParticipantMenu ? (React__namespace.createElement(DefaultParticipantMenu, { participant: participant })) : null)))) : (React__namespace.createElement(VideoMutedIndicator, { isSmallCell: false, displayName: (participant === null || participant === void 0 ? void 0 : participant.displayName) || "Guest", withRoundedCorners: true }))));
2177
+ }
2178
+ }
2179
+ function Grid({ renderParticipant, renderFloatingParticipant, stageParticipantLimit, gridGap, videoGridGap, enableSubgrid, enableParticipantMenu, enableConstrainedGrid, }) {
2180
+ const gridRef = React__namespace.useRef(null);
2181
+ const { containerFrame, cellViewsFloating, cellViewsVideoGrid, cellViewsInPresentationGrid, cellViewsInSubgrid, clientAspectRatios, videoStage, setContainerBounds, setClientAspectRatios, maximizedParticipant, setMaximizedParticipant, isConstrained, floatingParticipant, setFloatingParticipant, } = useGrid({
2182
+ activeVideosSubgridTrigger: 12,
2183
+ stageParticipantLimit,
2184
+ gridGap,
2185
+ videoGridGap,
2186
+ enableSubgrid,
2187
+ enableConstrainedGrid,
2188
+ });
2189
+ const handleSetClientAspectRatio = React__namespace.useCallback(({ aspectRatio, clientId }) => {
2190
+ setClientAspectRatios((prev) => (Object.assign(Object.assign({}, prev), { [clientId]: aspectRatio })));
2191
+ }, [setClientAspectRatios]);
2192
+ const floatingContent = React__namespace.useMemo(() => {
2193
+ return renderCellView(Object.assign({ cellView: cellViewsFloating[0], enableParticipantMenu }, (renderFloatingParticipant
2194
+ ? { render: ({ participant }) => renderFloatingParticipant({ participant }) }
2195
+ : {})));
2196
+ }, [cellViewsFloating]);
2197
+ const presentationGridContent = React__namespace.useMemo(() => cellViewsInPresentationGrid.map((cellView) => renderCellView(Object.assign({ cellView,
2198
+ enableParticipantMenu }, (renderParticipant ? { render: ({ participant }) => renderParticipant({ participant }) } : {})))), [cellViewsInPresentationGrid]);
2199
+ const gridContent = React__namespace.useMemo(() => cellViewsVideoGrid.map((cellView) => renderCellView(Object.assign({ cellView,
2200
+ enableParticipantMenu }, (renderParticipant ? { render: ({ participant }) => renderParticipant({ participant }) } : {})))), [cellViewsVideoGrid]);
2201
+ const subgridContent = React__namespace.useMemo(() => cellViewsInSubgrid.map((cellView) => renderCellView({
2202
+ cellView,
2203
+ enableParticipantMenu,
2204
+ })), [cellViewsInSubgrid]);
2205
+ React__namespace.useEffect(() => {
2206
+ if (!gridRef.current) {
2207
+ return;
2208
+ }
2209
+ const resizeObserver = new ResizeObserver(core.debounce(() => {
2210
+ var _a, _b, _c, _d;
2211
+ setContainerBounds({
2212
+ width: (_b = (_a = gridRef.current) === null || _a === void 0 ? void 0 : _a.clientWidth) !== null && _b !== void 0 ? _b : 640,
2213
+ height: (_d = (_c = gridRef.current) === null || _c === void 0 ? void 0 : _c.clientHeight) !== null && _d !== void 0 ? _d : 480,
2214
+ });
2215
+ }, { delay: 60, edges: false }));
2216
+ resizeObserver.observe(gridRef.current);
2217
+ return () => {
2218
+ resizeObserver.disconnect();
2219
+ };
2220
+ }, []);
2221
+ return (React__namespace.createElement(GridContext.Provider, { value: {
2222
+ onSetClientAspectRatio: handleSetClientAspectRatio,
2223
+ cellViewsVideoGrid,
2224
+ cellViewsInPresentationGrid,
2225
+ cellViewsInSubgrid,
2226
+ clientAspectRatios,
2227
+ maximizedParticipant,
2228
+ setMaximizedParticipant,
2229
+ floatingParticipant,
2230
+ setFloatingParticipant,
2231
+ isConstrained,
2232
+ } },
2233
+ React__namespace.createElement("div", { ref: gridRef, style: {
2234
+ width: "100%",
2235
+ height: "100%",
2236
+ position: "relative",
2237
+ } },
2238
+ React__namespace.createElement(VideoStageLayout, { containerFrame: containerFrame, floatingContent: floatingContent, layoutVideoStage: videoStage, presentationGridContent: presentationGridContent, gridContent: gridContent, subgridContent: subgridContent }))));
2239
+ }
2240
+
2241
+ exports.GridCell = GridCell;
2242
+ exports.GridVideoView = GridVideoView;
2243
+ exports.ParticipantMenu = ParticipantMenu;
2244
+ exports.ParticipantMenuContent = ParticipantMenuContent;
2245
+ exports.ParticipantMenuItem = ParticipantMenuItem;
2246
+ exports.ParticipantMenuTrigger = ParticipantMenuTrigger;
2247
+ exports.VideoGrid = Grid;
2248
+ exports.VideoView = VideoView;
2249
+ exports.WherebyProvider = Provider;
2250
+ exports.useLocalMedia = useLocalMedia;
2251
+ exports.useRoomConnection = useRoomConnection;