feat(popover): create a wrapper around InlineDialog
This commit is contained in:
parent
c54879d605
commit
85f0ad2791
|
@ -0,0 +1,33 @@
|
|||
/**
|
||||
* Mousemove padding styles are used to add invisible elements to the popover
|
||||
* to allow mouse movement from the popover trigger to the popover itself
|
||||
* without triggering a mouseleave event.
|
||||
*/
|
||||
.popover-mousemove-padding-bottom {
|
||||
bottom: -15px;
|
||||
height: 20px;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
width: 100%;
|
||||
}
|
||||
.popover-mousemove-padding-right {
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
right: -20;
|
||||
top: 0;
|
||||
width: 40px;
|
||||
}
|
||||
|
||||
/**
|
||||
* An invisible element is added to the top of the popover to ensure the mouse
|
||||
* stays over the popover when the popover's height is shrunk, which would then
|
||||
* normally leave the mouse outside of the popover itself and cause a mouseleave
|
||||
* event.
|
||||
*/
|
||||
.popover-mouse-padding-top {
|
||||
height: 30px;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: -25px;
|
||||
width: 100%;
|
||||
}
|
|
@ -378,28 +378,6 @@
|
|||
.remote-video-menu-trigger {
|
||||
margin-top: 7px;
|
||||
}
|
||||
.popover-mousemove-padding-bottom {
|
||||
bottom: -15px;
|
||||
height: 20px;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
width: 100%;
|
||||
}
|
||||
.popover-mousemove-padding-right {
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
right: -20;
|
||||
top: 0;
|
||||
width: 40px;
|
||||
}
|
||||
|
||||
.popover-mouse-top-padding {
|
||||
height: 30px;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: -25px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/**
|
||||
* Audio indicator on video thumbnails.
|
||||
|
|
|
@ -69,6 +69,7 @@
|
|||
@import 'aui-components/dropdown';
|
||||
@import '404';
|
||||
@import 'policy';
|
||||
@import 'popover';
|
||||
@import 'filmstrip';
|
||||
@import 'unsupported-browser/main';
|
||||
@import 'modals/invite/add-people';
|
||||
|
|
|
@ -0,0 +1,179 @@
|
|||
import InlineDialog from '@atlaskit/inline-dialog';
|
||||
import React, { Component } from 'react';
|
||||
|
||||
/**
|
||||
* A map of dialog positions, relative to trigger, to css classes used to
|
||||
* manipulate elements for handling mouse events.
|
||||
*
|
||||
* @private
|
||||
* @type {object}
|
||||
*/
|
||||
const DIALOG_TO_PADDING_POSITION = {
|
||||
'left': 'popover-mousemove-padding-right',
|
||||
'top': 'popover-mousemove-padding-bottom'
|
||||
};
|
||||
|
||||
/**
|
||||
* Takes the position expected by {@code InlineDialog} and maps it to a CSS
|
||||
* class that can be used styling the elements used for preventing mouseleave
|
||||
* events when moving from the trigger to the dialog.
|
||||
*
|
||||
* @param {string} position - From which position the dialog will display.
|
||||
* @private
|
||||
* @returns {string}
|
||||
*/
|
||||
function _mapPositionToPaddingClass(position = 'left') {
|
||||
return DIALOG_TO_PADDING_POSITION[position.split(' ')[0]];
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements a React {@code Component} for showing an {@code InlineDialog} on
|
||||
* mouseenter of the trigger and contents, and hiding the dialog on mouseleave.
|
||||
*
|
||||
* @extends Component
|
||||
*/
|
||||
class Popover extends Component {
|
||||
/**
|
||||
* Default values for {@code Popover} component's properties.
|
||||
*
|
||||
* @static
|
||||
*/
|
||||
static defaultProps = {
|
||||
className: '',
|
||||
id: ''
|
||||
};
|
||||
|
||||
/**
|
||||
* {@code Popover} component's property types.
|
||||
*
|
||||
* @static
|
||||
*/
|
||||
static propTypes = {
|
||||
/**
|
||||
* A child React Element to use as the trigger for showing the dialog.
|
||||
*/
|
||||
children: React.PropTypes.object,
|
||||
|
||||
/**
|
||||
* Additional CSS classnames to apply to the root of the {@code Popover}
|
||||
* component.
|
||||
*/
|
||||
className: React.PropTypes.string,
|
||||
|
||||
/**
|
||||
* The ReactElement to display within the dialog.
|
||||
*/
|
||||
content: React.PropTypes.object,
|
||||
|
||||
/**
|
||||
* An id attribute to apply to the root of the {@code Popover}
|
||||
* component.
|
||||
*/
|
||||
id: React.PropTypes.string,
|
||||
|
||||
/**
|
||||
* Callback to invoke when the popover has opened.
|
||||
*/
|
||||
onPopoverOpen: React.PropTypes.func,
|
||||
|
||||
/**
|
||||
* From which side of the dialog trigger the dialog should display. The
|
||||
* value will be passed to {@code InlineDialog}.
|
||||
*/
|
||||
position: React.PropTypes.string
|
||||
};
|
||||
|
||||
/**
|
||||
* Initializes a new {@code Popover} instance.
|
||||
*
|
||||
* @param {Object} props - The read-only properties with which the new
|
||||
* instance is to be initialized.
|
||||
*/
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
/**
|
||||
* Whether or not the {@code InlineDialog} should be displayed.
|
||||
*
|
||||
* @type {boolean}
|
||||
*/
|
||||
showDialog: false
|
||||
};
|
||||
|
||||
// Bind event handlers so they are only bound once for every instance.
|
||||
this._onHideDialog = this._onHideDialog.bind(this);
|
||||
this._onShowDialog = this._onShowDialog.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements React's {@link Component#render()}.
|
||||
*
|
||||
* @inheritdoc
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
render() {
|
||||
return (
|
||||
<div
|
||||
className = { this.props.className }
|
||||
id = { this.props.id }
|
||||
onMouseEnter = { this._onShowDialog }
|
||||
onMouseLeave = { this._onHideDialog }>
|
||||
<InlineDialog
|
||||
content = { this._renderContent() }
|
||||
isOpen = { this.state.showDialog }
|
||||
position = { this.props.position }>
|
||||
{ this.props.children }
|
||||
</InlineDialog>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops displaying the {@code InlineDialog}.
|
||||
*
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
_onHideDialog() {
|
||||
this.setState({ showDialog: false });
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the {@code InlineDialog} and calls any registered onPopoverOpen
|
||||
* callbacks.
|
||||
*
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
_onShowDialog() {
|
||||
this.setState({ showDialog: true });
|
||||
|
||||
if (this.props.onPopoverOpen) {
|
||||
this.props.onPopoverOpen();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the React Element to be displayed in the {@code InlineDialog}.
|
||||
* Also adds padding to support moving the mouse from the trigger to the
|
||||
* dialog to prevent mouseleave events.
|
||||
*
|
||||
* @private
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
_renderContent() {
|
||||
const { content, position } = this.props;
|
||||
|
||||
return (
|
||||
<div className = 'popover'>
|
||||
{ content }
|
||||
<div className = 'popover-mouse-padding-top' />
|
||||
<div className = { _mapPositionToPaddingClass(position) } />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Popover;
|
|
@ -0,0 +1 @@
|
|||
export { default as Popover } from './Popover';
|
|
@ -0,0 +1 @@
|
|||
export * from './components';
|
|
@ -1,7 +1,7 @@
|
|||
import { default as Popover } from '@atlaskit/inline-dialog';
|
||||
import React, { Component } from 'react';
|
||||
|
||||
import { JitsiParticipantConnectionStatus } from '../../base/lib-jitsi-meet';
|
||||
import { Popover } from '../../base/popover';
|
||||
import { ConnectionStatsTable } from '../../connection-stats';
|
||||
|
||||
import statsEmitter from '../statsEmitter';
|
||||
|
@ -123,8 +123,6 @@ class ConnectionIndicator extends Component {
|
|||
};
|
||||
|
||||
// Bind event handlers so they are only bound once for every instance.
|
||||
this._onHideStats = this._onHideStats.bind(this);
|
||||
this._onShowStats = this._onShowStats.bind(this);
|
||||
this._onStatsUpdated = this._onStatsUpdated.bind(this);
|
||||
this._onToggleShowMore = this._onToggleShowMore.bind(this);
|
||||
}
|
||||
|
@ -174,48 +172,21 @@ class ConnectionIndicator extends Component {
|
|||
*/
|
||||
render() {
|
||||
return (
|
||||
<div
|
||||
<Popover
|
||||
className = 'indicator-container'
|
||||
onMouseEnter = { this._onShowStats }
|
||||
onMouseLeave = { this._onHideStats }>
|
||||
<Popover
|
||||
content = { this._renderStatisticsTable() }
|
||||
isOpen = { this.state.showStats }
|
||||
position = { this.props.statsPopoverPosition }>
|
||||
<div className = 'popover-trigger'>
|
||||
<div className = 'connection-indicator indicator'>
|
||||
<div className = 'connection indicatoricon'>
|
||||
{ this._renderIcon() }
|
||||
</div>
|
||||
content = { this._renderStatisticsTable() }
|
||||
position = { this.props.statsPopoverPosition }>
|
||||
<div className = 'popover-trigger'>
|
||||
<div className = 'connection-indicator indicator'>
|
||||
<div className = 'connection indicatoricon'>
|
||||
{ this._renderIcon() }
|
||||
</div>
|
||||
</div>
|
||||
</Popover>
|
||||
</div>
|
||||
</div>
|
||||
</Popover>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the state not to show the Statistics Table popover.
|
||||
*
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
_onHideStats() {
|
||||
this.setState({ showStats: false });
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the state to show the Statistics Table popover.
|
||||
*
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
_onShowStats() {
|
||||
if (this.props.enableStatsDisplay) {
|
||||
this.setState({ showStats: true });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback invoked when new connection stats associated with the passed in
|
||||
* user ID are available. Will update the component's display of current
|
||||
|
@ -295,9 +266,7 @@ class ConnectionIndicator extends Component {
|
|||
}
|
||||
|
||||
/**
|
||||
* Creates a {@code ConnectionStatisticsTable} instance and an empty div
|
||||
* for preventing mouseleave events when moving from the icon to the
|
||||
* popover.
|
||||
* Creates a {@code ConnectionStatisticsTable} instance.
|
||||
*
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
|
@ -312,23 +281,16 @@ class ConnectionIndicator extends Component {
|
|||
} = this.state.stats;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<ConnectionStatsTable
|
||||
bandwidth = { bandwidth }
|
||||
bitrate = { bitrate }
|
||||
framerate = { framerate }
|
||||
isLocalVideo = { this.props.isLocalVideo }
|
||||
onShowMore = { this._onToggleShowMore }
|
||||
packetLoss = { packetLoss }
|
||||
resolution = { resolution }
|
||||
shouldShowMore = { this.state.showMoreStats }
|
||||
transport = { transport } />
|
||||
<div className = 'popover-mouse-top-padding' />
|
||||
<div
|
||||
className = { interfaceConfig.VERTICAL_FILMSTRIP
|
||||
? 'popover-mousemove-padding-right'
|
||||
: 'popover-mousemove-padding-bottom' } />
|
||||
</div>
|
||||
<ConnectionStatsTable
|
||||
bandwidth = { bandwidth }
|
||||
bitrate = { bitrate }
|
||||
framerate = { framerate }
|
||||
isLocalVideo = { this.props.isLocalVideo }
|
||||
onShowMore = { this._onToggleShowMore }
|
||||
packetLoss = { packetLoss }
|
||||
resolution = { resolution }
|
||||
shouldShowMore = { this.state.showMoreStats }
|
||||
transport = { transport } />
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import { default as Popover } from '@atlaskit/inline-dialog';
|
||||
import React, { Component } from 'react';
|
||||
|
||||
import {
|
||||
|
@ -9,6 +8,8 @@ import {
|
|||
VolumeSlider
|
||||
} from './';
|
||||
|
||||
import { Popover } from '../../base/popover';
|
||||
|
||||
declare var $: Object;
|
||||
declare var interfaceConfig: Object;
|
||||
|
||||
|
@ -73,10 +74,6 @@ class RemoteVideoMenuTriggerButton extends Component {
|
|||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
showRemoteMenu: false
|
||||
};
|
||||
|
||||
/**
|
||||
* The internal reference to topmost DOM/HTML element backing the React
|
||||
* {@code Component}. Accessed directly for associating an element as
|
||||
|
@ -87,8 +84,7 @@ class RemoteVideoMenuTriggerButton extends Component {
|
|||
*/
|
||||
this._rootElement = null;
|
||||
|
||||
// Bind event handlers so they are only bound once for every instance.
|
||||
this._onHideRemoteMenu = this._onHideRemoteMenu.bind(this);
|
||||
// Bind event handler so it is only bound once for every instance.
|
||||
this._onShowRemoteMenu = this._onShowRemoteMenu.bind(this);
|
||||
}
|
||||
|
||||
|
@ -106,35 +102,21 @@ class RemoteVideoMenuTriggerButton extends Component {
|
|||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
onMouseEnter = { this._onShowRemoteMenu }
|
||||
onMouseLeave = { this._onHideRemoteMenu }>
|
||||
<Popover
|
||||
content = { content }
|
||||
isOpen = { this.state.showRemoteMenu }
|
||||
position = { interfaceConfig.VERTICAL_FILMSTRIP
|
||||
? 'left middle' : 'top center' }>
|
||||
<span
|
||||
className = 'popover-trigger remote-video-menu-trigger'>
|
||||
<i
|
||||
className = 'icon-thumb-menu'
|
||||
title = 'Remote user controls' />
|
||||
</span>
|
||||
</Popover>
|
||||
</div>
|
||||
<Popover
|
||||
content = { content }
|
||||
onPopoverOpen = { this._onShowRemoteMenu }
|
||||
position = { interfaceConfig.VERTICAL_FILMSTRIP
|
||||
? 'left middle' : 'top center' }>
|
||||
<span
|
||||
className = 'popover-trigger remote-video-menu-trigger'>
|
||||
<i
|
||||
className = 'icon-thumb-menu'
|
||||
title = 'Remote user controls' />
|
||||
</span>
|
||||
</Popover>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the {@code RemoteVideoMenu}.
|
||||
*
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
_onHideRemoteMenu() {
|
||||
this.setState({ showRemoteMenu: false });
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the {@code RemoteVideoMenu}.
|
||||
*
|
||||
|
@ -143,8 +125,6 @@ class RemoteVideoMenuTriggerButton extends Component {
|
|||
*/
|
||||
_onShowRemoteMenu() {
|
||||
this.props.onMenuDisplay();
|
||||
|
||||
this.setState({ showRemoteMenu: true });
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -172,13 +152,11 @@ class RemoteVideoMenuTriggerButton extends Component {
|
|||
<MuteButton
|
||||
isAudioMuted = { isAudioMuted }
|
||||
key = 'mute'
|
||||
onClick = { this._onHideRemoteMenu }
|
||||
participantID = { participantID } />
|
||||
);
|
||||
buttons.push(
|
||||
<KickButton
|
||||
key = 'kick'
|
||||
onClick = { this._onHideRemoteMenu }
|
||||
participantID = { participantID } />
|
||||
);
|
||||
}
|
||||
|
@ -204,15 +182,9 @@ class RemoteVideoMenuTriggerButton extends Component {
|
|||
|
||||
if (buttons.length > 0) {
|
||||
return (
|
||||
<div>
|
||||
<RemoteVideoMenu id = { participantID }>
|
||||
{ buttons }
|
||||
</RemoteVideoMenu>
|
||||
<div
|
||||
className = { interfaceConfig.VERTICAL_FILMSTRIP
|
||||
? 'popover-mousemove-padding-right'
|
||||
: 'popover-mousemove-padding-bottom' } />
|
||||
</div>
|
||||
<RemoteVideoMenu id = { participantID }>
|
||||
{ buttons }
|
||||
</RemoteVideoMenu>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
import { default as Popover } from '@atlaskit/inline-dialog';
|
||||
import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { translate } from '../../base/i18n';
|
||||
|
||||
import { Popover } from '../../base/popover';
|
||||
import { VideoQualityDialog } from './';
|
||||
|
||||
import {
|
||||
|
@ -89,13 +88,6 @@ export class VideoQualityLabel extends Component {
|
|||
super(props);
|
||||
|
||||
this.state = {
|
||||
/**
|
||||
* Whether or not the {@code VideoQualityDialog} is displayed.
|
||||
*
|
||||
* @type {boolean}
|
||||
*/
|
||||
showVideoQualityDialog: false,
|
||||
|
||||
/**
|
||||
* Whether or not the filmstrip is transitioning from not visible
|
||||
* to visible. Used to set a transition class for animation.
|
||||
|
@ -104,10 +96,6 @@ export class VideoQualityLabel extends Component {
|
|||
*/
|
||||
togglingToVisible: false
|
||||
};
|
||||
|
||||
// Bind event handlers so they are only bound once for every instance.
|
||||
this._onHideQualityDialog = this._onHideQualityDialog.bind(this);
|
||||
this._onShowQualityDialog = this._onShowQualityDialog.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -161,23 +149,18 @@ export class VideoQualityLabel extends Component {
|
|||
= `${baseClasses} ${filmstrip} ${remoteVideosVisible} ${opening}`;
|
||||
|
||||
return (
|
||||
<div
|
||||
<Popover
|
||||
className = { classNames }
|
||||
content = { <VideoQualityDialog /> }
|
||||
id = 'videoResolutionLabel'
|
||||
onMouseEnter = { this._onShowQualityDialog }
|
||||
onMouseLeave = { this._onHideQualityDialog }>
|
||||
<Popover
|
||||
content = { this._renderQualityDialog() }
|
||||
isOpen = { this.state.showVideoQualityDialog }
|
||||
position = { 'left top' }>
|
||||
<div
|
||||
className = 'video-quality-label-status'>
|
||||
{ _audioOnly
|
||||
? <i className = 'icon-visibility-off' />
|
||||
: this._mapResolutionToTranslation(_resolution) }
|
||||
</div>
|
||||
</Popover>
|
||||
</div>
|
||||
position = { 'left top' }>
|
||||
<div
|
||||
className = 'video-quality-label-status'>
|
||||
{ _audioOnly
|
||||
? <i className = 'icon-visibility-off' />
|
||||
: this._mapResolutionToTranslation(_resolution) }
|
||||
</div>
|
||||
</Popover>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -209,41 +192,6 @@ export class VideoQualityLabel extends Component {
|
|||
return this.props.t(
|
||||
RESOLUTION_TO_TRANSLATION_KEY[highestMatchingResolution]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the {@code VideoQualityDialog}.
|
||||
*
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
_onShowQualityDialog() {
|
||||
this.setState({ showVideoQualityDialog: true });
|
||||
}
|
||||
|
||||
/**
|
||||
* Hides the {@code VideoQualityDialog}.
|
||||
*
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
_onHideQualityDialog() {
|
||||
this.setState({ showVideoQualityDialog: false });
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a React Element for choosing a maximum receive video quality.
|
||||
*
|
||||
* @private
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
_renderQualityDialog() {
|
||||
return (
|
||||
<div>
|
||||
<VideoQualityDialog />
|
||||
<div className = 'popover-mousemove-padding-right' />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in New Issue