2021-09-23 21:34:36 +08:00
|
|
|
import React from 'react';
|
|
|
|
import PropTypes from 'prop-types';
|
|
|
|
import { layoutSelect, layoutSelectInput } from '/imports/ui/components/layout/context';
|
|
|
|
import DEFAULT_VALUES from '/imports/ui/components/layout/defaultValues';
|
2021-09-24 21:33:15 +08:00
|
|
|
import { LAYOUT_TYPE, DEVICE_TYPE } from '/imports/ui/components/layout/enums';
|
2021-09-23 21:34:36 +08:00
|
|
|
|
|
|
|
import CustomLayout from '/imports/ui/components/layout/layout-manager/customLayout';
|
|
|
|
import SmartLayout from '/imports/ui/components/layout/layout-manager/smartLayout';
|
|
|
|
import PresentationFocusLayout from '/imports/ui/components/layout/layout-manager/presentationFocusLayout';
|
|
|
|
import VideoFocusLayout from '/imports/ui/components/layout/layout-manager/videoFocusLayout';
|
2023-11-24 15:07:41 +08:00
|
|
|
import CamerasOnlyLayout from '/imports/ui/components/layout/layout-manager/camerasOnly';
|
|
|
|
import PresentationOnlyLayout from '/imports/ui/components/layout/layout-manager/presentationOnlyLayout';
|
2023-11-28 18:41:24 +08:00
|
|
|
import ParticipantsAndChatOnlyLayout from '/imports/ui/components/layout/layout-manager/participantsAndChatOnlyLayout';
|
2023-04-05 23:37:56 +08:00
|
|
|
import { isPresentationEnabled } from '/imports/ui/services/features';
|
2021-09-23 21:34:36 +08:00
|
|
|
|
|
|
|
const propTypes = {
|
|
|
|
layoutType: PropTypes.string.isRequired,
|
|
|
|
};
|
|
|
|
|
2021-09-24 04:46:50 +08:00
|
|
|
const LayoutEngine = ({ layoutType }) => {
|
2021-09-23 21:34:36 +08:00
|
|
|
const bannerBarInput = layoutSelectInput((i) => i.bannerBar);
|
|
|
|
const notificationsBarInput = layoutSelectInput((i) => i.notificationsBar);
|
|
|
|
const cameraDockInput = layoutSelectInput((i) => i.cameraDock);
|
|
|
|
const presentationInput = layoutSelectInput((i) => i.presentation);
|
2021-09-24 21:33:15 +08:00
|
|
|
const actionbarInput = layoutSelectInput((i) => i.actionBar);
|
2023-11-28 22:45:12 +08:00
|
|
|
const navBarInput = layoutSelectInput((i) => i.navBar);
|
2021-09-24 21:33:15 +08:00
|
|
|
const sidebarNavigationInput = layoutSelectInput((i) => i.sidebarNavigation);
|
|
|
|
const sidebarContentInput = layoutSelectInput((i) => i.sidebarContent);
|
2022-03-17 22:06:21 +08:00
|
|
|
const externalVideoInput = layoutSelectInput((i) => i.externalVideo);
|
2024-02-19 18:59:45 +08:00
|
|
|
const genericComponentInput = layoutSelectInput((i) => i.genericComponent);
|
2022-03-17 22:06:21 +08:00
|
|
|
const screenShareInput = layoutSelectInput((i) => i.screenShare);
|
2023-04-05 23:37:56 +08:00
|
|
|
const sharedNotesInput = layoutSelectInput((i) => i.sharedNotes);
|
2021-09-23 21:34:36 +08:00
|
|
|
|
|
|
|
const fullscreen = layoutSelect((i) => i.fullscreen);
|
|
|
|
const isRTL = layoutSelect((i) => i.isRTL);
|
2021-09-24 04:43:02 +08:00
|
|
|
const fontSize = layoutSelect((i) => i.fontSize);
|
2021-09-24 21:33:15 +08:00
|
|
|
const deviceType = layoutSelect((i) => i.deviceType);
|
2021-09-23 21:34:36 +08:00
|
|
|
|
2021-09-24 21:33:15 +08:00
|
|
|
const isMobile = deviceType === DEVICE_TYPE.MOBILE;
|
2021-09-25 01:38:44 +08:00
|
|
|
const isTablet = deviceType === DEVICE_TYPE.TABLET;
|
2021-09-23 21:34:36 +08:00
|
|
|
const windowWidth = () => window.document.documentElement.clientWidth;
|
|
|
|
const windowHeight = () => window.document.documentElement.clientHeight;
|
2021-09-24 21:33:15 +08:00
|
|
|
const min = (value1, value2) => (value1 <= value2 ? value1 : value2);
|
|
|
|
const max = (value1, value2) => (value1 >= value2 ? value1 : value2);
|
2021-09-23 21:34:36 +08:00
|
|
|
|
|
|
|
const bannerAreaHeight = () => {
|
|
|
|
const { hasNotification } = notificationsBarInput;
|
|
|
|
const { hasBanner } = bannerBarInput;
|
|
|
|
const bannerHeight = hasBanner ? DEFAULT_VALUES.bannerHeight : 0;
|
|
|
|
const notificationHeight = hasNotification ? DEFAULT_VALUES.bannerHeight : 0;
|
|
|
|
|
|
|
|
return bannerHeight + notificationHeight;
|
|
|
|
};
|
|
|
|
|
|
|
|
const baseCameraDockBounds = (mediaAreaBounds, sidebarSize) => {
|
2023-04-05 23:37:56 +08:00
|
|
|
const { isOpen, slidesLength } = presentationInput;
|
2022-03-17 22:06:21 +08:00
|
|
|
const { hasExternalVideo } = externalVideoInput;
|
2024-02-19 18:59:45 +08:00
|
|
|
const { hasGenericComponent } = genericComponentInput;
|
2022-03-17 22:06:21 +08:00
|
|
|
const { hasScreenShare } = screenShareInput;
|
2023-04-05 23:37:56 +08:00
|
|
|
const { isPinned: isSharedNotesPinned } = sharedNotesInput;
|
2021-09-23 21:34:36 +08:00
|
|
|
|
|
|
|
const cameraDockBounds = {};
|
|
|
|
|
2023-05-12 04:20:26 +08:00
|
|
|
if (cameraDockInput.numCameras === 0 && layoutType !== LAYOUT_TYPE.VIDEO_FOCUS) {
|
2021-09-23 21:34:36 +08:00
|
|
|
cameraDockBounds.width = 0;
|
|
|
|
cameraDockBounds.height = 0;
|
|
|
|
|
|
|
|
return cameraDockBounds;
|
|
|
|
}
|
|
|
|
|
2023-11-28 22:45:12 +08:00
|
|
|
const navBarHeight = calculatesNavbarHeight();
|
2023-11-24 15:07:41 +08:00
|
|
|
const hasPresentation = isPresentationEnabled() && slidesLength !== 0;
|
|
|
|
const isGeneralMediaOff = !hasPresentation
|
2024-02-19 18:59:45 +08:00
|
|
|
&& !hasExternalVideo && !hasScreenShare
|
|
|
|
&& !isSharedNotesPinned && !hasGenericComponent;
|
2022-03-08 04:21:58 +08:00
|
|
|
|
2023-04-05 23:37:56 +08:00
|
|
|
if (!isOpen || isGeneralMediaOff) {
|
2021-09-23 21:34:36 +08:00
|
|
|
cameraDockBounds.width = mediaAreaBounds.width;
|
|
|
|
cameraDockBounds.maxWidth = mediaAreaBounds.width;
|
2022-01-26 00:56:52 +08:00
|
|
|
cameraDockBounds.height = mediaAreaBounds.height - bannerAreaHeight();
|
2021-09-23 21:34:36 +08:00
|
|
|
cameraDockBounds.maxHeight = mediaAreaBounds.height;
|
2023-11-28 22:45:12 +08:00
|
|
|
cameraDockBounds.top = navBarHeight + bannerAreaHeight();
|
2021-09-23 21:34:36 +08:00
|
|
|
cameraDockBounds.left = !isRTL ? mediaAreaBounds.left : 0;
|
|
|
|
cameraDockBounds.right = isRTL ? sidebarSize : null;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fullscreen.group === 'webcams') {
|
|
|
|
cameraDockBounds.width = windowWidth();
|
|
|
|
cameraDockBounds.minWidth = windowWidth();
|
|
|
|
cameraDockBounds.maxWidth = windowWidth();
|
|
|
|
cameraDockBounds.height = windowHeight();
|
|
|
|
cameraDockBounds.minHeight = windowHeight();
|
|
|
|
cameraDockBounds.maxHeight = windowHeight();
|
|
|
|
cameraDockBounds.top = 0;
|
|
|
|
cameraDockBounds.left = 0;
|
|
|
|
cameraDockBounds.right = 0;
|
|
|
|
cameraDockBounds.zIndex = 99;
|
|
|
|
|
|
|
|
return cameraDockBounds;
|
|
|
|
}
|
|
|
|
|
|
|
|
return cameraDockBounds;
|
|
|
|
};
|
|
|
|
|
2023-11-28 22:45:12 +08:00
|
|
|
const calculatesNavbarHeight = () => {
|
|
|
|
const { navBarHeight } = DEFAULT_VALUES;
|
|
|
|
|
|
|
|
return navBarInput.hasNavBar ? navBarHeight : 0;
|
|
|
|
};
|
|
|
|
|
2021-09-24 04:43:02 +08:00
|
|
|
const calculatesNavbarBounds = (mediaAreaBounds) => {
|
2023-11-28 22:45:12 +08:00
|
|
|
const { navBarTop } = DEFAULT_VALUES;
|
|
|
|
|
|
|
|
const navBarHeight = calculatesNavbarHeight();
|
|
|
|
|
|
|
|
if (!navBarInput.hasNavBar) {
|
|
|
|
return {
|
|
|
|
width: 0,
|
|
|
|
height: 0,
|
|
|
|
top: 0,
|
|
|
|
left: 0,
|
|
|
|
zIndex: 0,
|
|
|
|
};
|
|
|
|
}
|
2021-09-24 04:43:02 +08:00
|
|
|
|
|
|
|
return {
|
|
|
|
width: mediaAreaBounds.width,
|
|
|
|
height: navBarHeight,
|
|
|
|
top: navBarTop + bannerAreaHeight(),
|
|
|
|
left: !isRTL ? mediaAreaBounds.left : 0,
|
|
|
|
zIndex: 1,
|
|
|
|
};
|
2021-09-24 21:33:15 +08:00
|
|
|
};
|
2021-09-24 04:43:02 +08:00
|
|
|
|
|
|
|
const calculatesActionbarHeight = () => {
|
2023-11-28 22:45:12 +08:00
|
|
|
if (!actionbarInput.hasActionBar) {
|
|
|
|
return {
|
|
|
|
height: 0,
|
|
|
|
innerHeight: 0,
|
|
|
|
padding: 0,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2021-09-24 04:43:02 +08:00
|
|
|
const { actionBarHeight, actionBarPadding } = DEFAULT_VALUES;
|
|
|
|
|
|
|
|
const BASE_FONT_SIZE = 14; // 90% font size
|
|
|
|
const height = ((actionBarHeight / BASE_FONT_SIZE) * fontSize);
|
|
|
|
|
|
|
|
return {
|
|
|
|
height: height + (actionBarPadding * 2),
|
|
|
|
innerHeight: height,
|
|
|
|
padding: actionBarPadding,
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
2021-09-24 21:33:15 +08:00
|
|
|
const calculatesActionbarBounds = (mediaAreaBounds) => {
|
|
|
|
const actionBarHeight = calculatesActionbarHeight();
|
|
|
|
|
|
|
|
return {
|
|
|
|
display: actionbarInput.hasActionBar,
|
|
|
|
width: mediaAreaBounds.width,
|
|
|
|
height: actionBarHeight.height,
|
|
|
|
innerHeight: actionBarHeight.innerHeight,
|
|
|
|
padding: actionBarHeight.padding,
|
|
|
|
top: windowHeight() - actionBarHeight.height,
|
|
|
|
left: !isRTL ? mediaAreaBounds.left : 0,
|
|
|
|
zIndex: 1,
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
const calculatesSidebarNavWidth = () => {
|
|
|
|
const {
|
|
|
|
sidebarNavMinWidth,
|
|
|
|
sidebarNavMaxWidth,
|
|
|
|
} = DEFAULT_VALUES;
|
|
|
|
|
|
|
|
const { isOpen, width: sidebarNavWidth } = sidebarNavigationInput;
|
|
|
|
|
|
|
|
let minWidth = 0;
|
|
|
|
let width = 0;
|
|
|
|
let maxWidth = 0;
|
|
|
|
if (isOpen) {
|
|
|
|
if (isMobile) {
|
|
|
|
minWidth = windowWidth();
|
|
|
|
width = windowWidth();
|
|
|
|
maxWidth = windowWidth();
|
|
|
|
} else {
|
|
|
|
if (sidebarNavWidth === 0) {
|
|
|
|
width = min(max((windowWidth() * 0.2), sidebarNavMinWidth), sidebarNavMaxWidth);
|
|
|
|
} else {
|
|
|
|
width = min(max(sidebarNavWidth, sidebarNavMinWidth), sidebarNavMaxWidth);
|
|
|
|
}
|
|
|
|
minWidth = sidebarNavMinWidth;
|
|
|
|
maxWidth = sidebarNavMaxWidth;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return {
|
|
|
|
minWidth,
|
|
|
|
width,
|
|
|
|
maxWidth,
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
const calculatesSidebarNavHeight = () => {
|
|
|
|
const { navBarHeight } = DEFAULT_VALUES;
|
|
|
|
const { isOpen } = sidebarNavigationInput;
|
|
|
|
|
|
|
|
let sidebarNavHeight = 0;
|
|
|
|
if (isOpen) {
|
|
|
|
if (isMobile) {
|
|
|
|
sidebarNavHeight = windowHeight() - navBarHeight - bannerAreaHeight();
|
|
|
|
} else {
|
|
|
|
sidebarNavHeight = windowHeight() - bannerAreaHeight();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return sidebarNavHeight;
|
|
|
|
};
|
|
|
|
|
|
|
|
const calculatesSidebarNavBounds = () => {
|
|
|
|
const { sidebarNavTop, navBarHeight, sidebarNavLeft } = DEFAULT_VALUES;
|
|
|
|
|
|
|
|
let top = sidebarNavTop + bannerAreaHeight();
|
|
|
|
|
|
|
|
if (isMobile) {
|
|
|
|
top = navBarHeight + bannerAreaHeight();
|
|
|
|
}
|
|
|
|
|
|
|
|
return {
|
|
|
|
top,
|
|
|
|
left: !isRTL ? sidebarNavLeft : null,
|
|
|
|
right: isRTL ? sidebarNavLeft : null,
|
|
|
|
zIndex: isMobile ? 11 : 2,
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
const calculatesSidebarContentWidth = () => {
|
|
|
|
const {
|
|
|
|
sidebarContentMinWidth,
|
|
|
|
sidebarContentMaxWidth,
|
|
|
|
} = DEFAULT_VALUES;
|
|
|
|
|
|
|
|
const { isOpen, width: sidebarContentWidth } = sidebarContentInput;
|
|
|
|
|
|
|
|
let minWidth = 0;
|
|
|
|
let width = 0;
|
|
|
|
let maxWidth = 0;
|
|
|
|
|
|
|
|
if (isOpen) {
|
|
|
|
if (isMobile) {
|
|
|
|
minWidth = windowWidth();
|
|
|
|
width = windowWidth();
|
|
|
|
maxWidth = windowWidth();
|
|
|
|
} else {
|
|
|
|
if (sidebarContentWidth === 0) {
|
|
|
|
width = min(
|
|
|
|
max((windowWidth() * 0.2), sidebarContentMinWidth), sidebarContentMaxWidth,
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
width = min(max(sidebarContentWidth, sidebarContentMinWidth),
|
|
|
|
sidebarContentMaxWidth);
|
|
|
|
}
|
|
|
|
minWidth = sidebarContentMinWidth;
|
|
|
|
maxWidth = sidebarContentMaxWidth;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return {
|
|
|
|
minWidth,
|
|
|
|
width,
|
|
|
|
maxWidth,
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
const calculatesSidebarContentBounds = (sidebarNavWidth) => {
|
|
|
|
const { navBarHeight, sidebarNavTop } = DEFAULT_VALUES;
|
|
|
|
|
|
|
|
let top = sidebarNavTop + bannerAreaHeight();
|
|
|
|
|
|
|
|
if (isMobile) top = navBarHeight + bannerAreaHeight();
|
|
|
|
|
|
|
|
let left = isMobile ? 0 : sidebarNavWidth;
|
|
|
|
let right = isMobile ? 0 : sidebarNavWidth;
|
|
|
|
left = !isRTL ? left : null;
|
|
|
|
right = isRTL ? right : null;
|
|
|
|
|
|
|
|
const zIndex = isMobile ? 11 : 1;
|
|
|
|
|
|
|
|
return {
|
|
|
|
top,
|
|
|
|
left,
|
|
|
|
right,
|
|
|
|
zIndex,
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
const calculatesMediaAreaBounds = (sidebarNavWidth, sidebarContentWidth) => {
|
|
|
|
const { height: actionBarHeight } = calculatesActionbarHeight();
|
2023-11-28 22:45:12 +08:00
|
|
|
const navBarHeight = calculatesNavbarHeight();
|
|
|
|
|
2021-09-24 21:33:15 +08:00
|
|
|
let left = 0;
|
|
|
|
let width = 0;
|
|
|
|
if (isMobile) {
|
|
|
|
width = windowWidth();
|
|
|
|
} else {
|
|
|
|
left = !isRTL ? sidebarNavWidth + sidebarContentWidth : 0;
|
|
|
|
width = windowWidth() - sidebarNavWidth - sidebarContentWidth;
|
|
|
|
}
|
|
|
|
|
|
|
|
return {
|
|
|
|
width,
|
|
|
|
height: windowHeight() - (navBarHeight + actionBarHeight + bannerAreaHeight()),
|
|
|
|
top: navBarHeight + bannerAreaHeight(),
|
|
|
|
left,
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
2021-09-23 21:34:36 +08:00
|
|
|
const common = {
|
|
|
|
bannerAreaHeight,
|
|
|
|
baseCameraDockBounds,
|
2023-11-28 22:45:12 +08:00
|
|
|
calculatesNavbarHeight,
|
2021-09-24 04:43:02 +08:00
|
|
|
calculatesNavbarBounds,
|
|
|
|
calculatesActionbarHeight,
|
2021-09-24 21:33:15 +08:00
|
|
|
calculatesActionbarBounds,
|
|
|
|
calculatesSidebarNavWidth,
|
|
|
|
calculatesSidebarNavHeight,
|
|
|
|
calculatesSidebarNavBounds,
|
|
|
|
calculatesSidebarContentWidth,
|
|
|
|
calculatesSidebarContentBounds,
|
|
|
|
calculatesMediaAreaBounds,
|
2021-09-25 01:38:44 +08:00
|
|
|
isMobile,
|
|
|
|
isTablet,
|
2021-09-23 21:34:36 +08:00
|
|
|
};
|
2023-11-24 15:07:41 +08:00
|
|
|
|
2022-07-07 04:03:04 +08:00
|
|
|
const layout = document.getElementById('layout');
|
2021-09-23 21:34:36 +08:00
|
|
|
|
|
|
|
switch (layoutType) {
|
|
|
|
case LAYOUT_TYPE.CUSTOM_LAYOUT:
|
2023-11-24 15:07:41 +08:00
|
|
|
layout?.setAttribute('data-layout', LAYOUT_TYPE.CUSTOM_LAYOUT);
|
2021-09-23 21:34:36 +08:00
|
|
|
return <CustomLayout {...common} />;
|
|
|
|
case LAYOUT_TYPE.SMART_LAYOUT:
|
2023-11-24 15:07:41 +08:00
|
|
|
layout?.setAttribute('data-layout', LAYOUT_TYPE.SMART_LAYOUT);
|
2021-09-23 21:34:36 +08:00
|
|
|
return <SmartLayout {...common} />;
|
|
|
|
case LAYOUT_TYPE.PRESENTATION_FOCUS:
|
2023-11-24 15:07:41 +08:00
|
|
|
layout?.setAttribute('data-layout', LAYOUT_TYPE.PRESENTATION_FOCUS);
|
2021-09-23 21:34:36 +08:00
|
|
|
return <PresentationFocusLayout {...common} />;
|
|
|
|
case LAYOUT_TYPE.VIDEO_FOCUS:
|
2023-11-24 15:07:41 +08:00
|
|
|
layout?.setAttribute('data-layout', LAYOUT_TYPE.VIDEO_FOCUS);
|
2021-09-23 21:34:36 +08:00
|
|
|
return <VideoFocusLayout {...common} />;
|
2023-11-24 15:07:41 +08:00
|
|
|
case LAYOUT_TYPE.CAMERAS_ONLY:
|
|
|
|
layout?.setAttribute('data-layout', LAYOUT_TYPE.CAMERAS_ONLY);
|
|
|
|
return <CamerasOnlyLayout {...common} />;
|
|
|
|
case LAYOUT_TYPE.PRESENTATION_ONLY:
|
|
|
|
layout?.setAttribute('data-layout', LAYOUT_TYPE.PRESENTATION_ONLY);
|
|
|
|
return <PresentationOnlyLayout {...common} />;
|
2023-11-28 18:41:24 +08:00
|
|
|
case LAYOUT_TYPE.PARTICIPANTS_AND_CHAT_ONLY:
|
|
|
|
layout?.setAttribute('data-layout', LAYOUT_TYPE.PARTICIPANTS_AND_CHAT_ONLY);
|
|
|
|
return <ParticipantsAndChatOnlyLayout {...common} />;
|
2021-09-23 21:34:36 +08:00
|
|
|
default:
|
2023-11-24 15:07:41 +08:00
|
|
|
layout?.setAttribute('data-layout', LAYOUT_TYPE.CUSTOM_LAYOUT);
|
2021-09-23 21:34:36 +08:00
|
|
|
return <CustomLayout {...common} />;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2021-09-24 04:46:50 +08:00
|
|
|
LayoutEngine.propTypes = propTypes;
|
2021-09-23 21:34:36 +08:00
|
|
|
|
2021-09-24 04:46:50 +08:00
|
|
|
export default LayoutEngine;
|