diff --git a/css/_drawer.scss b/css/_drawer.scss index 8f78e372f..fd142d51f 100644 --- a/css/_drawer.scss +++ b/css/_drawer.scss @@ -5,6 +5,10 @@ bottom: 0; z-index: $drawerZ; border-radius: 16px 16px 0 0; + + &.notification-portal { + z-index: $dropdownZ; + } } .drawer-portal::after { diff --git a/css/_notifications.scss b/css/_notifications.scss new file mode 100644 index 000000000..740602203 --- /dev/null +++ b/css/_notifications.scss @@ -0,0 +1,23 @@ +.notification-appear, .notification-enter { + opacity: 0; + position: relative; + left: -200px; + transition: all .2s !important; // !important needed to overwrite atlaskit default style + + &-active { + opacity: 1; + left: 0; + } +} + +.notification-exit { + opacity: 1; + position: relative; + left: 0; + transition: all .2s !important; // !important needed to overwrite atlaskit default style + + &-active { + opacity: 0; + left: -200px; + } +} diff --git a/css/main.scss b/css/main.scss index 61fb78944..79d558c8d 100644 --- a/css/main.scss +++ b/css/main.scss @@ -106,5 +106,6 @@ $flagsImagePath: "../images/"; @import 'reactions-menu'; @import 'plan-limit'; @import 'polls'; +@import 'notifications'; /* Modules END */ diff --git a/react/features/base/popover/components/Popover.web.js b/react/features/base/popover/components/Popover.web.js index d8017b551..12593405b 100644 --- a/react/features/base/popover/components/Popover.web.js +++ b/react/features/base/popover/components/Popover.web.js @@ -2,7 +2,7 @@ import React, { Component } from 'react'; -import { Drawer, DrawerPortal, DialogPortal } from '../../../toolbox/components/web'; +import { Drawer, JitsiPortal, DialogPortal } from '../../../toolbox/components/web'; import { isMobileBrowser } from '../../environment/utils'; import { getContextMenuStyle } from '../functions.web'; @@ -173,13 +173,13 @@ class Popover extends Component { id = { id } onClick = { this._onShowDialog }> { children } - + { content } - + ); } diff --git a/react/features/conference/components/web/Conference.js b/react/features/conference/components/web/Conference.js index a930f1e3a..f87d09cd5 100644 --- a/react/features/conference/components/web/Conference.js +++ b/react/features/conference/components/web/Conference.js @@ -19,7 +19,7 @@ import { ParticipantsPane } from '../../../participants-pane/components/web'; import { getParticipantsPaneOpen } from '../../../participants-pane/functions'; import { Prejoin, isPrejoinPageVisible, isPrejoinPageLoading } from '../../../prejoin'; import { fullScreenChanged, showToolbox } from '../../../toolbox/actions.web'; -import { Toolbox } from '../../../toolbox/components/web'; +import { JitsiPortal, Toolbox } from '../../../toolbox/components/web'; import { LAYOUTS, getCurrentLayout } from '../../../video-layout'; import { maybeShowSuboptimalExperienceNotification } from '../../functions'; import { @@ -86,6 +86,11 @@ type Props = AbstractProps & { */ _mouseMoveCallbackInterval: number, + /** + *Whether or not the notifications should be displayed in the overflow drawer. + */ + _overflowDrawer: boolean, + /** * Name for this conference room. */ @@ -209,6 +214,8 @@ class Conference extends AbstractConference { const { _isParticipantsPaneVisible, _layoutClassName, + _notificationsVisible, + _overflowDrawer, _showLobby, _showPrejoin } = this.props; @@ -239,7 +246,12 @@ class Conference extends AbstractConference { { _showPrejoin || _showLobby || } - { this.renderNotificationsContainer() } + {_notificationsVisible && (_overflowDrawer + ? + {this.renderNotificationsContainer({ portal: true })} + + : this.renderNotificationsContainer()) + } @@ -368,6 +380,7 @@ class Conference extends AbstractConference { */ function _mapStateToProps(state) { const { backgroundAlpha, mouseMoveCallbackInterval } = state['features/base/config']; + const { overflowDrawer } = state['features/toolbox']; return { ...abstractMapStateToProps(state), @@ -375,6 +388,7 @@ function _mapStateToProps(state) { _isParticipantsPaneVisible: getParticipantsPaneOpen(state), _layoutClassName: LAYOUT_CLASSNAMES[getCurrentLayout(state)], _mouseMoveCallbackInterval: mouseMoveCallbackInterval, + _overflowDrawer: overflowDrawer, _roomName: getConferenceNameForTitle(state), _showLobby: getIsLobbyVisible(state), _showPrejoin: isPrejoinPageVisible(state) || isPrejoinPageLoading(state) diff --git a/react/features/notifications/components/AbstractNotificationsContainer.js b/react/features/notifications/components/AbstractNotificationsContainer.js deleted file mode 100644 index c72b02798..000000000 --- a/react/features/notifications/components/AbstractNotificationsContainer.js +++ /dev/null @@ -1,188 +0,0 @@ -// @flow - -import { Component } from 'react'; - -import { hideNotification } from '../actions'; -import { areThereNotifications } from '../functions'; - -export type Props = { - - /** - * The notifications to be displayed, with the first index being the - * notification at the top and the rest shown below it in order. - */ - _notifications: Array, - - /** - * The length, in milliseconds, to use as a default timeout for all - * dismissable timeouts that do not have a timeout specified. - */ - autoDismissTimeout: number, - - /** - * Invoked to update the redux store in order to remove notifications. - */ - dispatch: Function -}; - -declare var interfaceConfig: Object; - -/** - * Abstract class for {@code NotificationsContainer} component. - */ -export default class AbstractNotificationsContainer - extends Component

{ - /** - * A timeout id returned by setTimeout. - */ - _notificationDismissTimeout: ?TimeoutID; - - /** - * Initializes a new {@code AbstractNotificationsContainer} instance. - * - * @inheritdoc - */ - constructor(props: P) { - super(props); - - /** - * The timeout set for automatically dismissing a displayed - * notification. This value is set on the instance and not state to - * avoid additional re-renders. - * - * @type {number|null} - */ - this._notificationDismissTimeout = null; - - // Bind event handlers so they are only bound once for every instance. - this._onDismissed = this._onDismissed.bind(this); - } - - /** - * Sets a timeout for the first notification (if applicable). - * - * @inheritdoc - */ - componentDidMount() { - // Set the initial dismiss timeout (if any) - this._manageDismissTimeout(); - } - - /** - * Sets a timeout if the currently displayed notification has changed. - * - * @inheritdoc - */ - componentDidUpdate(prevProps: P) { - this._manageDismissTimeout(prevProps); - } - - /** - * Sets/clears the dismiss timeout for the top notification. - * - * @param {P} [prevProps] - The previous properties (if called from - * {@code componentDidUpdate}). - * @returns {void} - * @private - */ - _manageDismissTimeout(prevProps: ?P) { - const { _notifications, autoDismissTimeout } = this.props; - - if (_notifications.length) { - const notification = _notifications[0]; - const previousNotification - = prevProps && prevProps._notifications.length - ? prevProps._notifications[0] - : undefined; - - if (notification !== previousNotification) { - this._clearNotificationDismissTimeout(); - - if (notification - && (notification.timeout - || typeof autoDismissTimeout === 'number') - && notification.props.isDismissAllowed !== false) { - const { - timeout = autoDismissTimeout, - uid - } = notification; - - this._notificationDismissTimeout = setTimeout(() => { - // Perform a no-op if a timeout is not specified. - this._onDismissed(uid); - }, timeout); - } - } - } else if (this._notificationDismissTimeout) { - // Clear timeout when all notifications are cleared (e.g external - // call to clear them) - this._clearNotificationDismissTimeout(); - } - } - - /** - * Clear any dismissal timeout that is still active. - * - * @inheritdoc - * returns {void} - */ - componentWillUnmount() { - this._clearNotificationDismissTimeout(); - } - - _onDismissed: number => void; - - /** - * Clears the running notification dismiss timeout, if any. - * - * @returns {void} - */ - _clearNotificationDismissTimeout() { - this._notificationDismissTimeout - && clearTimeout(this._notificationDismissTimeout); - - this._notificationDismissTimeout = null; - } - - /** - * Emits an action to remove the notification from the redux store so it - * stops displaying. - * - * @param {number} uid - The id of the notification to be removed. - * @private - * @returns {void} - */ - _onDismissed(uid) { - const { _notifications } = this.props; - - // Clear the timeout only if it's the top notification that's being - // dismissed (the timeout is set only for the top one). - if (!_notifications.length || _notifications[0].uid === uid) { - this._clearNotificationDismissTimeout(); - } - - this.props.dispatch(hideNotification(uid)); - } -} - -/** - * Maps (parts of) the Redux state to the associated NotificationsContainer's - * props. - * - * @param {Object} state - The Redux state. - * @private - * @returns {{ - * _notifications: Array - * }} - */ -export function _abstractMapStateToProps(state: Object) { - const { notifications } = state['features/notifications']; - const _visible = areThereNotifications(state); - - return { - _notifications: _visible ? notifications : [], - autoDismissTimeout: typeof interfaceConfig === 'undefined' - ? undefined // Ignore for the case of mobile - : interfaceConfig.ENFORCE_NOTIFICATION_AUTO_DISMISS_TIMEOUT - }; -} diff --git a/react/features/notifications/components/native/NotificationsContainer.js b/react/features/notifications/components/native/NotificationsContainer.js index 7952fa989..b4a747f0a 100644 --- a/react/features/notifications/components/native/NotificationsContainer.js +++ b/react/features/notifications/components/native/NotificationsContainer.js @@ -1,18 +1,27 @@ // @flow -import React from 'react'; +import React, { Component } from 'react'; import { View } from 'react-native'; import { connect } from '../../../base/redux'; -import AbstractNotificationsContainer, { - _abstractMapStateToProps, - type Props as AbstractProps -} from '../AbstractNotificationsContainer'; +import { hideNotification } from '../../actions'; +import { areThereNotifications } from '../../functions'; import Notification from './Notification'; import styles from './styles'; -type Props = AbstractProps & { +type Props = { + + /** + * The notifications to be displayed, with the first index being the + * notification at the top and the rest shown below it in order. + */ + _notifications: Array, + + /** + * Invoked to update the redux store in order to remove notifications. + */ + dispatch: Function, /** * Any custom styling applied to the notifications container. @@ -27,8 +36,134 @@ type Props = AbstractProps & { * * @extends {Component} */ -class NotificationsContainer - extends AbstractNotificationsContainer { +class NotificationsContainer extends Component { + + /** + * A timeout id returned by setTimeout. + */ + _notificationDismissTimeout: ?TimeoutID; + + /** + * Initializes a new {@code NotificationsContainer} instance. + * + * @inheritdoc + */ + constructor(props: Props) { + super(props); + + /** + * The timeout set for automatically dismissing a displayed + * notification. This value is set on the instance and not state to + * avoid additional re-renders. + * + * @type {number|null} + */ + this._notificationDismissTimeout = null; + + // Bind event handlers so they are only bound once for every instance. + this._onDismissed = this._onDismissed.bind(this); + } + + /** + * Sets a timeout for the first notification (if applicable). + * + * @inheritdoc + */ + componentDidMount() { + // Set the initial dismiss timeout (if any) + this._manageDismissTimeout(); + } + + /** + * Sets a timeout if the currently displayed notification has changed. + * + * @inheritdoc + */ + componentDidUpdate(prevProps: Props) { + this._manageDismissTimeout(prevProps); + } + + /** + * Sets/clears the dismiss timeout for the top notification. + * + * @param {P} [prevProps] - The previous properties (if called from + * {@code componentDidUpdate}). + * @returns {void} + * @private + */ + _manageDismissTimeout(prevProps: ?Props) { + const { _notifications } = this.props; + + if (_notifications.length) { + const notification = _notifications[0]; + const previousNotification + = prevProps && prevProps._notifications.length + ? prevProps._notifications[0] + : undefined; + + if (notification !== previousNotification) { + this._clearNotificationDismissTimeout(); + + if (notification && notification.timeout && notification.props.isDismissAllowed !== false) { + const { + timeout, + uid + } = notification; + + this._notificationDismissTimeout = setTimeout(() => { + // Perform a no-op if a timeout is not specified. + this._onDismissed(uid); + }, timeout); + } + } + } else if (this._notificationDismissTimeout) { + // Clear timeout when all notifications are cleared (e.g external + // call to clear them) + this._clearNotificationDismissTimeout(); + } + } + + /** + * Clear any dismissal timeout that is still active. + * + * @inheritdoc + */ + componentWillUnmount() { + this._clearNotificationDismissTimeout(); + } + + /** + * Clears the running notification dismiss timeout, if any. + * + * @returns {void} + */ + _clearNotificationDismissTimeout() { + this._notificationDismissTimeout && clearTimeout(this._notificationDismissTimeout); + + this._notificationDismissTimeout = null; + } + + _onDismissed: number => void; + + /** + * Emits an action to remove the notification from the redux store so it + * stops displaying. + * + * @param {number} uid - The id of the notification to be removed. + * @private + * @returns {void} + */ + _onDismissed(uid) { + const { _notifications } = this.props; + + // Clear the timeout only if it's the top notification that's being + // dismissed (the timeout is set only for the top one). + if (!_notifications.length || _notifications[0].uid === uid) { + this._clearNotificationDismissTimeout(); + } + + this.props.dispatch(hideNotification(uid)); + } /** * Implements React's {@link Component#render()}. @@ -39,8 +174,7 @@ class NotificationsContainer const { _notifications } = this.props; // Currently the native container displays only the topmost notification - const theNotification - = _notifications && _notifications.length && _notifications[0]; + const theNotification = _notifications[0]; if (!theNotification) { return null; @@ -64,4 +198,21 @@ class NotificationsContainer _onDismissed: number => void; } -export default connect(_abstractMapStateToProps)(NotificationsContainer); +/** + * Maps (parts of) the Redux state to the associated NotificationsContainer's + * props. + * + * @param {Object} state - The Redux state. + * @private + * @returns {Props} + */ +export function mapStateToProps(state: Object) { + const { notifications } = state['features/notifications']; + const _visible = areThereNotifications(state); + + return { + _notifications: _visible ? notifications : [] + }; +} + +export default connect(mapStateToProps)(NotificationsContainer); diff --git a/react/features/notifications/components/web/Notification.js b/react/features/notifications/components/web/Notification.js index 6a40e58a4..df02db073 100644 --- a/react/features/notifications/components/web/Notification.js +++ b/react/features/notifications/components/web/Notification.js @@ -2,12 +2,10 @@ import Flag from '@atlaskit/flag'; import EditorInfoIcon from '@atlaskit/icon/glyph/editor/info'; -import ErrorIcon from '@atlaskit/icon/glyph/error'; -import WarningIcon from '@atlaskit/icon/glyph/warning'; -import { colors } from '@atlaskit/theme'; import React from 'react'; import { translate } from '../../../base/i18n'; +import { colors } from '../../../base/ui/Tokens'; import { NOTIFICATION_TYPE } from '../../constants'; import AbstractNotification, { type Props @@ -21,11 +19,9 @@ declare var interfaceConfig: Object; * @type {{error, info, normal, success, warning}} */ const ICON_COLOR = { - error: colors.R400, - info: colors.N500, - normal: colors.N0, - success: colors.G400, - warning: colors.Y200 + error: colors.error06, + normal: colors.primary06, + warning: colors.warning05 }; /** @@ -42,7 +38,6 @@ class Notification extends AbstractNotification { */ render() { const { - appearance, hideErrorSupportLink, t, title, @@ -54,7 +49,6 @@ class Notification extends AbstractNotification { return ( { const secIconColor = ICON_COLOR[this.props.appearance]; const iconSize = 'medium'; - switch (appearance) { - case NOTIFICATION_TYPE.ERROR: - return ( - - ); - - case NOTIFICATION_TYPE.WARNING: - return ( - - ); - - default: - return ( - - ); - } + return <> +
+ + ; } } diff --git a/react/features/notifications/components/web/NotificationsContainer.js b/react/features/notifications/components/web/NotificationsContainer.js index 67e6db73c..769182f96 100644 --- a/react/features/notifications/components/web/NotificationsContainer.js +++ b/react/features/notifications/components/web/NotificationsContainer.js @@ -1,38 +1,173 @@ // @flow -import { FlagGroup } from '@atlaskit/flag'; -import React from 'react'; +import { FlagGroupContext } from '@atlaskit/flag/flag-group'; +import { AtlasKitThemeProvider } from '@atlaskit/theme'; +import { withStyles } from '@material-ui/styles'; +import React, { Component } from 'react'; +import { CSSTransition, TransitionGroup } from 'react-transition-group'; import { translate } from '../../../base/i18n'; import { connect } from '../../../base/redux'; -import AbstractNotificationsContainer, { - _abstractMapStateToProps, - type Props as AbstractProps -} from '../AbstractNotificationsContainer'; +import { hideNotification } from '../../actions'; +import { areThereNotifications } from '../../functions'; import Notification from './Notification'; -type Props = AbstractProps & { +declare var interfaceConfig: Object; + +type Props = { /** * Whether we are a SIP gateway or not. */ - _iAmSipGateway: boolean, + _iAmSipGateway: boolean, - /** + /** + * Whether or not the chat is open. + */ + _isChatOpen: boolean, + + /** + * The notifications to be displayed, with the first index being the + * notification at the top and the rest shown below it in order. + */ + _notifications: Array, + + /** + * The length, in milliseconds, to use as a default timeout for all + * dismissible timeouts that do not have a timeout specified. + */ + autoDismissTimeout: number, + + /** + * JSS classes object. + */ + classes: Object, + + /** + * Invoked to update the redux store in order to remove notifications. + */ + dispatch: Function, + + /** + * Whether or not the notifications are displayed in a portal. + */ + portal?: boolean, + + /** * Invoked to obtain translated strings. */ - t: Function + t: Function +}; + +const useStyles = theme => { + return { + container: { + position: 'absolute', + left: '16px', + bottom: '90px', + width: '400px', + maxWidth: '100%', + zIndex: 600 + }, + + containerPortal: { + maxWidth: 'calc(100% - 32px)' + }, + + containerChatOpen: { + left: '331px' + }, + + transitionGroup: { + '& > *': { + marginBottom: '20px', + borderRadius: '6px!important', // !important used to overwrite atlaskit style + position: 'relative' + }, + + '& div > span > svg > path': { + fill: 'inherit' + }, + + '& div > span, & div > p': { + color: theme.palette.field01 + }, + + '& .ribbon': { + width: '4px', + height: 'calc(100% - 16px)', + position: 'absolute', + left: 0, + top: '8px', + borderRadius: '4px', + + '&.normal': { + backgroundColor: theme.palette.link01Active + }, + + '&.error': { + backgroundColor: theme.palette.iconError + }, + + '&.warning': { + backgroundColor: theme.palette.warning01 + } + } + } + }; }; /** * Implements a React {@link Component} which displays notifications and handles - * automatic dismissmal after a notification is shown for a defined timeout + * automatic dismissal after a notification is shown for a defined timeout * period. * * @extends {Component} */ -class NotificationsContainer extends AbstractNotificationsContainer { +class NotificationsContainer extends Component { + _api: Object; + _timeouts: Map; + + /** + * Initializes a new {@code NotificationsContainer} instance. + * + * @inheritdoc + */ + constructor(props: Props) { + super(props); + + this._timeouts = new Map(); + + // Bind event handlers so they are only bound once for every instance. + this._onDismissed = this._onDismissed.bind(this); + + // HACK ALERT! We are rendering AtlasKit Flag elements outside of a FlagGroup container. + // In order to hook-up the dismiss action we'll a fake context provider, + // just like FlagGroup does. + this._api = { + onDismissed: this._onDismissed, + dismissAllowed: () => true + }; + } + + /** + * Sets a timeout for each notification, where applicable. + * + * @inheritdoc + */ + componentDidMount() { + this._updateTimeouts(); + } + + /** + * Sets a timeout for each notification, where applicable. + * + * @inheritdoc + */ + componentDidUpdate() { + this._updateTimeouts(); + } /** * Implements React's {@link Component#render()}. @@ -46,17 +181,46 @@ class NotificationsContainer extends AbstractNotificationsContainer { } return ( - - { this._renderFlags() } - + + +
+ + {this._renderFlags()} + +
+
+
); } _onDismissed: number => void; + /** + * Emits an action to remove the notification from the redux store so it + * stops displaying. + * + * @param {number} uid - The id of the notification to be removed. + * @private + * @returns {void} + */ + _onDismissed(uid) { + const timeout = this._timeouts.get(uid); + + if (timeout) { + clearTimeout(timeout); + this._timeouts.delete(uid); + } + + this.props.dispatch(hideNotification(uid)); + } + /** * Renders notifications to display as ReactElements. An empty array will * be returned if notifications are disabled. @@ -74,15 +238,46 @@ class NotificationsContainer extends AbstractNotificationsContainer { // either id or key to set a key on notifications, but accessing // props.key will cause React to print an error. return ( - + timeout = { 200 }> + + ); }); } + + /** + * Updates the timeouts for every notification. + * + * @returns {void} + */ + _updateTimeouts() { + const { _notifications, autoDismissTimeout } = this.props; + + for (const notification of _notifications) { + if ((notification.timeout || typeof autoDismissTimeout === 'number') + && notification.props.isDismissAllowed !== false + && !this._timeouts.has(notification.uid)) { + const { + timeout = autoDismissTimeout, + uid + } = notification; + const timerID = setTimeout(() => { + this._onDismissed(uid); + }, timeout); + + this._timeouts.set(uid, timerID); + } + } + } } /** @@ -93,13 +288,17 @@ class NotificationsContainer extends AbstractNotificationsContainer { * @returns {Props} */ function _mapStateToProps(state) { + const { notifications } = state['features/notifications']; const { iAmSipGateway } = state['features/base/config']; + const { isOpen: isChatOpen } = state['features/chat']; + const _visible = areThereNotifications(state); return { - ..._abstractMapStateToProps(state), - _iAmSipGateway: Boolean(iAmSipGateway) + _iAmSipGateway: Boolean(iAmSipGateway), + _isChatOpen: isChatOpen, + _notifications: _visible ? notifications : [], + autoDismissTimeout: interfaceConfig.ENFORCE_NOTIFICATION_AUTO_DISMISS_TIMEOUT }; } - -export default translate(connect(_mapStateToProps)(NotificationsContainer)); +export default translate(connect(_mapStateToProps)(withStyles(useStyles)(NotificationsContainer))); diff --git a/react/features/participants-pane/components/web/LobbyParticipants.js b/react/features/participants-pane/components/web/LobbyParticipants.js index bac73d543..f99e6b8b5 100644 --- a/react/features/participants-pane/components/web/LobbyParticipants.js +++ b/react/features/participants-pane/components/web/LobbyParticipants.js @@ -10,7 +10,7 @@ import { Icon, IconCheck, IconClose } from '../../../base/icons'; import { withPixelLineHeight } from '../../../base/styles/functions.web'; import { admitMultiple } from '../../../lobby/actions.web'; import { getLobbyEnabled, getKnockingParticipants } from '../../../lobby/functions'; -import { Drawer, DrawerPortal } from '../../../toolbox/components/web'; +import { Drawer, JitsiPortal } from '../../../toolbox/components/web'; import { showOverflowDrawer } from '../../../toolbox/functions'; import { useLobbyActions, useParticipantDrawer } from '../../hooks'; @@ -96,7 +96,7 @@ export default function LobbyParticipants() { openDrawerForParticipant = { openDrawerForParticipant } overflowDrawer = { overflowDrawer } participants = { participants } /> - + @@ -128,7 +128,7 @@ export default function LobbyParticipants() { - + ); } diff --git a/react/features/participants-pane/components/web/MeetingParticipantContextMenu.js b/react/features/participants-pane/components/web/MeetingParticipantContextMenu.js index 6cf03a7e3..b70a1593d 100644 --- a/react/features/participants-pane/components/web/MeetingParticipantContextMenu.js +++ b/react/features/participants-pane/components/web/MeetingParticipantContextMenu.js @@ -30,7 +30,7 @@ import { withPixelLineHeight } from '../../../base/styles/functions.web'; import { isParticipantAudioMuted, isParticipantVideoMuted } from '../../../base/tracks'; import { openChatById } from '../../../chat/actions'; import { setVolume } from '../../../filmstrip/actions.web'; -import { Drawer, DrawerPortal } from '../../../toolbox/components/web'; +import { Drawer, JitsiPortal } from '../../../toolbox/components/web'; import { GrantModeratorDialog, KickRemoteParticipantDialog, MuteEveryoneDialog } from '../../../video-menu'; import { VolumeSlider } from '../../../video-menu/components/web'; import MuteRemoteParticipantsVideoDialog from '../../../video-menu/components/web/MuteRemoteParticipantsVideoDialog'; @@ -533,7 +533,7 @@ class MeetingParticipantContextMenu extends Component { { actions } } - + @@ -549,7 +549,7 @@ class MeetingParticipantContextMenu extends Component { { actions } - + ); } diff --git a/react/features/participants-pane/components/web/ParticipantsPane.js b/react/features/participants-pane/components/web/ParticipantsPane.js index e1055cbab..e714a9ee0 100644 --- a/react/features/participants-pane/components/web/ParticipantsPane.js +++ b/react/features/participants-pane/components/web/ParticipantsPane.js @@ -7,7 +7,7 @@ import { openDialog } from '../../../base/dialog'; import { translate } from '../../../base/i18n'; import { isLocalParticipantModerator } from '../../../base/participants'; import { connect } from '../../../base/redux'; -import { Drawer, DrawerPortal } from '../../../toolbox/components/web'; +import { Drawer, JitsiPortal } from '../../../toolbox/components/web'; import { showOverflowDrawer } from '../../../toolbox/functions'; import { MuteEveryoneDialog } from '../../../video-menu/components/'; import { close } from '../../actions'; @@ -166,13 +166,13 @@ class ParticipantsPane extends Component { )} - + - + ); diff --git a/react/features/toolbox/components/web/DrawerPortal.js b/react/features/toolbox/components/web/JitsiPortal.js similarity index 59% rename from react/features/toolbox/components/web/DrawerPortal.js rename to react/features/toolbox/components/web/JitsiPortal.js index bde20c0e7..64a09935f 100644 --- a/react/features/toolbox/components/web/DrawerPortal.js +++ b/react/features/toolbox/components/web/JitsiPortal.js @@ -8,7 +8,12 @@ type Props = { /** * The component(s) to be displayed within the drawer portal. */ - children: React$Node + children: React$Node, + + /** + * Class name used to add custom styles to the portal. + */ + className?: string }; /** @@ -17,12 +22,12 @@ type Props = { * * @returns {ReactElement} */ -function DrawerPortal({ children }: Props) { +function JitsiPortal({ children, className }: Props) { return ( - + { children } ); } -export default DrawerPortal; +export default JitsiPortal; diff --git a/react/features/toolbox/components/web/OverflowMenuButton.js b/react/features/toolbox/components/web/OverflowMenuButton.js index 71a78440a..a7aa6a074 100644 --- a/react/features/toolbox/components/web/OverflowMenuButton.js +++ b/react/features/toolbox/components/web/OverflowMenuButton.js @@ -12,7 +12,7 @@ import { type ReactionEmojiProps } from '../../../reactions/constants'; import { getReactionsQueue } from '../../../reactions/functions.any'; import Drawer from './Drawer'; -import DrawerPortal from './DrawerPortal'; +import JitsiPortal from './JitsiPortal'; import ToolbarButton from './ToolbarButton'; /** @@ -114,7 +114,7 @@ class OverflowMenuButton extends Component { overflowDrawer ? ( <> {this._renderToolbarButton()} - + @@ -128,7 +128,7 @@ class OverflowMenuButton extends Component { reaction = { reaction } uid = { uid } />))} } - + ) : (