fix(inline-dialog): reimplement popover display on mouse move

Create empty elements within InlineDialog content that can be
used to bridge mouse movement from the InlineDialog trigger to
the InlineDialog content. The empty elements are positioned
absolute so they can break out of the InlineDialog container
and not affect popper's position calculations.
This commit is contained in:
Leonard Kim 2017-08-22 10:42:35 -07:00 committed by yanas
parent 5f55b3198c
commit fdee6dc360
4 changed files with 121 additions and 75 deletions

View File

@ -378,6 +378,28 @@
.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.

View File

@ -1,4 +1,4 @@
import AKInlineDialog from '@atlaskit/inline-dialog';
import { default as Popover } from '@atlaskit/inline-dialog';
import React, { Component } from 'react';
import { JitsiParticipantConnectionStatus } from '../../base/lib-jitsi-meet';
@ -123,9 +123,8 @@ class ConnectionIndicator extends Component {
};
// Bind event handlers so they are only bound once for every instance.
this._onStatsUpdated = this._onStatsUpdated.bind(this);
this._onStatsClose = this._onStatsClose.bind(this);
this._onStatsToggle = this._onStatsToggle.bind(this);
this._onHideStats = this._onHideStats.bind(this);
this._onShowStats = this._onShowStats.bind(this);
this._onStatsUpdated = this._onStatsUpdated.bind(this);
this._onToggleShowMore = this._onToggleShowMore.bind(this);
}
@ -175,22 +174,22 @@ class ConnectionIndicator extends Component {
*/
render() {
return (
<div className = 'indicator-container'>
<AKInlineDialog
<div
className = 'indicator-container'
onMouseEnter = { this._onShowStats }
onMouseLeave = { this._onHideStats }>
<Popover
content = { this._renderStatisticsTable() }
isOpen = { this.state.showStats }
onClose = { this._onStatsClose }
position = { this.props.statsPopoverPosition }>
<div
className = 'popover-trigger'
onClick = { this._onStatsToggle }>
<div className = 'popover-trigger'>
<div className = 'connection-indicator indicator'>
<div className = 'connection indicatoricon'>
{ this._renderIcon() }
</div>
</div>
</div>
</AKInlineDialog>
</Popover>
</div>
);
}
@ -201,19 +200,19 @@ class ConnectionIndicator extends Component {
* @private
* @returns {void}
*/
_onStatsClose() {
_onHideStats() {
this.setState({ showStats: false });
}
/**
* Sets the state to show or hide the Statistics Table popover.
* Sets the state to show the Statistics Table popover.
*
* @private
* @returns {void}
*/
_onStatsToggle() {
_onShowStats() {
if (this.props.enableStatsDisplay) {
this.setState({ showStats: !this.state.showStats });
this.setState({ showStats: true });
}
}
@ -296,7 +295,9 @@ class ConnectionIndicator extends Component {
}
/**
* Creates a {@code ConnectionStatisticsTable} instance.
* Creates a {@code ConnectionStatisticsTable} instance and an empty div
* for preventing mouseleave events when moving from the icon to the
* popover.
*
* @returns {ReactElement}
*/
@ -311,16 +312,23 @@ class ConnectionIndicator extends Component {
} = this.state.stats;
return (
<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>
<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>
);
}
}

View File

@ -1,4 +1,4 @@
import AKInlineDialog from '@atlaskit/inline-dialog';
import { default as Popover } from '@atlaskit/inline-dialog';
import React, { Component } from 'react';
import {
@ -88,8 +88,8 @@ class RemoteVideoMenuTriggerButton extends Component {
this._rootElement = null;
// Bind event handlers so they are only bound once for every instance.
this._onRemoteMenuClose = this._onRemoteMenuClose.bind(this);
this._onRemoteMenuToggle = this._onRemoteMenuToggle.bind(this);
this._onHideRemoteMenu = this._onHideRemoteMenu.bind(this);
this._onShowRemoteMenu = this._onShowRemoteMenu.bind(this);
}
/**
@ -106,21 +106,22 @@ class RemoteVideoMenuTriggerButton extends Component {
}
return (
<AKInlineDialog
content = { content }
isOpen = { this.state.showRemoteMenu }
onClose = { this._onRemoteMenuClose }
position = { interfaceConfig.VERTICAL_FILMSTRIP
? 'left middle' : 'top center' }
shouldFlip = { true }>
<span
className = 'popover-trigger remote-video-menu-trigger'
onClick = { this._onRemoteMenuToggle }>
<i
className = 'icon-thumb-menu'
title = 'Remote user controls' />
</span>
</AKInlineDialog>
<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>
);
}
@ -130,24 +131,20 @@ class RemoteVideoMenuTriggerButton extends Component {
* @private
* @returns {void}
*/
_onRemoteMenuClose() {
_onHideRemoteMenu() {
this.setState({ showRemoteMenu: false });
}
/**
* Opens or closes the {@code RemoteVideoMenu}.
* Opens the {@code RemoteVideoMenu}.
*
* @private
* @returns {void}
*/
_onRemoteMenuToggle() {
const willShowRemoteMenu = !this.state.showRemoteMenu;
_onShowRemoteMenu() {
this.props.onMenuDisplay();
if (willShowRemoteMenu) {
this.props.onMenuDisplay();
}
this.setState({ showRemoteMenu: willShowRemoteMenu });
this.setState({ showRemoteMenu: true });
}
/**
@ -175,13 +172,13 @@ class RemoteVideoMenuTriggerButton extends Component {
<MuteButton
isAudioMuted = { isAudioMuted }
key = 'mute'
onClick = { this._onRemoteMenuClose }
onClick = { this._onHideRemoteMenu }
participantID = { participantID } />
);
buttons.push(
<KickButton
key = 'kick'
onClick = { this._onRemoteMenuClose }
onClick = { this._onHideRemoteMenu }
participantID = { participantID } />
);
}
@ -207,9 +204,15 @@ class RemoteVideoMenuTriggerButton extends Component {
if (buttons.length > 0) {
return (
<RemoteVideoMenu id = { participantID }>
{ buttons }
</RemoteVideoMenu>
<div>
<RemoteVideoMenu id = { participantID }>
{ buttons }
</RemoteVideoMenu>
<div
className = { interfaceConfig.VERTICAL_FILMSTRIP
? 'popover-mousemove-padding-right'
: 'popover-mousemove-padding-bottom' } />
</div>
);
}

View File

@ -1,4 +1,4 @@
import AKInlineDialog from '@atlaskit/inline-dialog';
import { default as Popover } from '@atlaskit/inline-dialog';
import React, { Component } from 'react';
import { connect } from 'react-redux';
@ -106,8 +106,8 @@ export class VideoQualityLabel extends Component {
};
// Bind event handlers so they are only bound once for every instance.
this._onDialogClose = this._onDialogClose.bind(this);
this._onDialogToggle = this._onDialogToggle.bind(this);
this._onHideQualityDialog = this._onHideQualityDialog.bind(this);
this._onShowQualityDialog = this._onShowQualityDialog.bind(this);
}
/**
@ -163,20 +163,20 @@ export class VideoQualityLabel extends Component {
return (
<div
className = { classNames }
id = 'videoResolutionLabel'>
<AKInlineDialog
content = { <VideoQualityDialog /> }
id = 'videoResolutionLabel'
onMouseEnter = { this._onShowQualityDialog }
onMouseLeave = { this._onHideQualityDialog }>
<Popover
content = { this._renderQualityDialog() }
isOpen = { this.state.showVideoQualityDialog }
onClose = { this._onDialogClose }
position = { 'left top' }>
<div
className = 'video-quality-label-status'
onClick = { this._onDialogToggle }>
className = 'video-quality-label-status'>
{ _audioOnly
? <i className = 'icon-visibility-off' />
: this._mapResolutionToTranslation(_resolution) }
</div>
</AKInlineDialog>
</Popover>
</div>
);
}
@ -211,26 +211,39 @@ export class VideoQualityLabel extends Component {
}
/**
* Toggles the display of the {@code VideoQualityDialog}.
* Shows the {@code VideoQualityDialog}.
*
* @private
* @returns {void}
*/
_onDialogToggle() {
this.setState({
showVideoQualityDialog: !this.state.showVideoQualityDialog
});
_onShowQualityDialog() {
this.setState({ showVideoQualityDialog: true });
}
/**
* Hides the attached inline dialog.
* Hides the {@code VideoQualityDialog}.
*
* @private
* @returns {void}
*/
_onDialogClose() {
_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>
);
}
}
/**