ref(popover) Update Popover to support click trigger (#12447)
Convert Popover to TS Update OverflowMenu to use Popover instead of InlineDialog
This commit is contained in:
parent
33ec975e19
commit
faec46dd17
|
@ -1,9 +1,11 @@
|
|||
/* @flow */
|
||||
import React, { Component } from 'react';
|
||||
import React, { Component, ReactNode } from 'react';
|
||||
|
||||
import { IReduxState } from '../../../app/types';
|
||||
// eslint-disable-next-line lines-around-comment
|
||||
// @ts-ignore
|
||||
import { DialogPortal, Drawer, JitsiPortal } from '../../../toolbox/components/web';
|
||||
import { isMobileBrowser } from '../../environment/utils';
|
||||
import { connect } from '../../redux';
|
||||
import { connect } from '../../redux/functions';
|
||||
import { getContextMenuStyle } from '../functions.web';
|
||||
|
||||
/**
|
||||
|
@ -14,55 +16,60 @@ type Props = {
|
|||
/**
|
||||
* A child React Element to use as the trigger for showing the dialog.
|
||||
*/
|
||||
children: React$Node,
|
||||
children: ReactNode;
|
||||
|
||||
/**
|
||||
* Additional CSS classnames to apply to the root of the {@code Popover}
|
||||
* component.
|
||||
*/
|
||||
className: string,
|
||||
className?: string;
|
||||
|
||||
/**
|
||||
* The ReactElement to display within the dialog.
|
||||
*/
|
||||
content: Object,
|
||||
content: ReactNode;
|
||||
|
||||
/**
|
||||
* Whether displaying of the popover should be prevented.
|
||||
*/
|
||||
disablePopover: boolean,
|
||||
disablePopover?: boolean;
|
||||
|
||||
/**
|
||||
* An id attribute to apply to the root of the {@code Popover}
|
||||
* component.
|
||||
*/
|
||||
id: string,
|
||||
id?: string;
|
||||
|
||||
/**
|
||||
* Callback to invoke when the popover has closed.
|
||||
*/
|
||||
onPopoverClose: Function,
|
||||
onPopoverClose: Function;
|
||||
|
||||
/**
|
||||
* Callback to invoke when the popover has opened.
|
||||
*/
|
||||
onPopoverOpen: Function,
|
||||
onPopoverOpen: Function;
|
||||
|
||||
/**
|
||||
* Whether to display the Popover as a drawer.
|
||||
*/
|
||||
overflowDrawer: boolean,
|
||||
overflowDrawer?: boolean;
|
||||
|
||||
/**
|
||||
* From which side of the dialog trigger the dialog should display. The
|
||||
* value will be passed to {@code InlineDialog}.
|
||||
*/
|
||||
position: string,
|
||||
position: string;
|
||||
|
||||
/**
|
||||
* Whether the trigger for open/ close should be click or hover.
|
||||
*/
|
||||
trigger?: 'hover' | 'click';
|
||||
|
||||
/**
|
||||
* Whether the popover is visible or not.
|
||||
*/
|
||||
visible: boolean
|
||||
visible: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -73,7 +80,12 @@ type State = {
|
|||
/**
|
||||
* The style to apply to the context menu in order to position it correctly.
|
||||
*/
|
||||
contextMenuStyle: Object
|
||||
contextMenuStyle?: {
|
||||
bottom?: string;
|
||||
left?: string;
|
||||
position: string;
|
||||
top?: string;
|
||||
} | null;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -90,13 +102,14 @@ class Popover extends Component<Props, State> {
|
|||
*/
|
||||
static defaultProps = {
|
||||
className: '',
|
||||
id: ''
|
||||
id: '',
|
||||
trigger: 'hover'
|
||||
};
|
||||
|
||||
/**
|
||||
* Reference to the dialog container.
|
||||
*/
|
||||
_containerRef: Object;
|
||||
_containerRef: React.RefObject<HTMLDivElement>;
|
||||
|
||||
_contextMenuRef: HTMLElement;
|
||||
|
||||
|
@ -119,11 +132,12 @@ class Popover extends Component<Props, State> {
|
|||
this._onKeyPress = this._onKeyPress.bind(this);
|
||||
this._containerRef = React.createRef();
|
||||
this._onEscKey = this._onEscKey.bind(this);
|
||||
this._onThumbClick = this._onThumbClick.bind(this);
|
||||
this._onClick = this._onClick.bind(this);
|
||||
this._onTouchStart = this._onTouchStart.bind(this);
|
||||
this._setContextMenuRef = this._setContextMenuRef.bind(this);
|
||||
this._setContextMenuStyle = this._setContextMenuStyle.bind(this);
|
||||
this._getCustomDialogStyle = this._getCustomDialogStyle.bind(this);
|
||||
this._onOutsideClick = this._onOutsideClick.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -134,6 +148,10 @@ class Popover extends Component<Props, State> {
|
|||
*/
|
||||
componentDidMount() {
|
||||
window.addEventListener('touchstart', this._onTouchStart);
|
||||
if (this.props.trigger === 'click') {
|
||||
// @ts-ignore
|
||||
window.addEventListener('click', this._onOutsideClick);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -144,6 +162,22 @@ class Popover extends Component<Props, State> {
|
|||
*/
|
||||
componentWillUnmount() {
|
||||
window.removeEventListener('touchstart', this._onTouchStart);
|
||||
if (this.props.trigger === 'click') {
|
||||
// @ts-ignore
|
||||
window.removeEventListener('click', this._onOutsideClick);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles click outside the popover.
|
||||
*
|
||||
* @param {MouseEvent} e - The click event.
|
||||
* @returns {void}
|
||||
*/
|
||||
_onOutsideClick(e: React.MouseEvent) { // @ts-ignore
|
||||
if (!this._containerRef?.current?.contains(e.target) && this.props.visible) {
|
||||
this._onHideDialog();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -153,7 +187,7 @@ class Popover extends Component<Props, State> {
|
|||
* @returns {ReactElement}
|
||||
*/
|
||||
render() {
|
||||
const { children, className, content, id, overflowDrawer, visible } = this.props;
|
||||
const { children, className, content, id, overflowDrawer, visible, trigger } = this.props;
|
||||
|
||||
if (overflowDrawer) {
|
||||
return (
|
||||
|
@ -177,10 +211,12 @@ class Popover extends Component<Props, State> {
|
|||
<div
|
||||
className = { className }
|
||||
id = { id }
|
||||
onClick = { this._onThumbClick }
|
||||
onClick = { this._onClick }
|
||||
onKeyPress = { this._onKeyPress }
|
||||
onMouseEnter = { this._onShowDialog }
|
||||
onMouseLeave = { this._onHideDialog }
|
||||
{ ...(trigger === 'hover' ? {
|
||||
onMouseEnter: this._onShowDialog,
|
||||
onMouseLeave: this._onHideDialog
|
||||
} : {}) }
|
||||
ref = { this._containerRef }>
|
||||
{ visible && (
|
||||
<DialogPortal
|
||||
|
@ -195,8 +231,6 @@ class Popover extends Component<Props, State> {
|
|||
);
|
||||
}
|
||||
|
||||
_setContextMenuStyle: (size: Object) => void;
|
||||
|
||||
/**
|
||||
* Sets the context menu dialog style for positioning it on screen.
|
||||
*
|
||||
|
@ -204,14 +238,12 @@ class Popover extends Component<Props, State> {
|
|||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
_setContextMenuStyle(size) {
|
||||
_setContextMenuStyle(size: DOMRectReadOnly) {
|
||||
const style = this._getCustomDialogStyle(size);
|
||||
|
||||
this.setState({ contextMenuStyle: style });
|
||||
}
|
||||
|
||||
_setContextMenuRef: (elem: HTMLElement) => void;
|
||||
|
||||
/**
|
||||
* Sets the context menu's ref.
|
||||
*
|
||||
|
@ -219,12 +251,10 @@ class Popover extends Component<Props, State> {
|
|||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
_setContextMenuRef(elem) {
|
||||
_setContextMenuRef(elem: HTMLElement) {
|
||||
this._contextMenuRef = elem;
|
||||
}
|
||||
|
||||
_onTouchStart: (event: TouchEvent) => void;
|
||||
|
||||
/**
|
||||
* Hide dialog on touch outside of the context menu.
|
||||
*
|
||||
|
@ -232,18 +262,16 @@ class Popover extends Component<Props, State> {
|
|||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
_onTouchStart(event) {
|
||||
_onTouchStart(event: TouchEvent) {
|
||||
if (this.props.visible
|
||||
&& !this.props.overflowDrawer
|
||||
&& this._contextMenuRef
|
||||
&& this._contextMenuRef.contains
|
||||
&& this._contextMenuRef.contains // @ts-ignore
|
||||
&& !this._contextMenuRef.contains(event.target)) {
|
||||
this._onHideDialog();
|
||||
}
|
||||
}
|
||||
|
||||
_onHideDialog: () => void;
|
||||
|
||||
/**
|
||||
* Stops displaying the {@code InlineDialog}.
|
||||
*
|
||||
|
@ -260,8 +288,6 @@ class Popover extends Component<Props, State> {
|
|||
}
|
||||
}
|
||||
|
||||
_onShowDialog: (Object) => void;
|
||||
|
||||
/**
|
||||
* Displays the {@code InlineDialog} and calls any registered onPopoverOpen
|
||||
* callbacks.
|
||||
|
@ -270,16 +296,14 @@ class Popover extends Component<Props, State> {
|
|||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
_onShowDialog(event) {
|
||||
event && event.stopPropagation();
|
||||
_onShowDialog(event?: React.MouseEvent | React.KeyboardEvent) {
|
||||
event?.stopPropagation();
|
||||
|
||||
if (!this.props.disablePopover) {
|
||||
this.props.onPopoverOpen();
|
||||
}
|
||||
}
|
||||
|
||||
_onThumbClick: (Object) => void;
|
||||
|
||||
/**
|
||||
* Prevents switching from tile view to stage view on accidentally clicking
|
||||
* the popover thumbs.
|
||||
|
@ -288,11 +312,18 @@ class Popover extends Component<Props, State> {
|
|||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
_onThumbClick(event) {
|
||||
event.stopPropagation();
|
||||
}
|
||||
_onClick(event: React.MouseEvent) {
|
||||
const { trigger, visible } = this.props;
|
||||
|
||||
_onKeyPress: (Object) => void;
|
||||
event.stopPropagation();
|
||||
if (trigger === 'click') {
|
||||
if (visible) {
|
||||
this._onHideDialog();
|
||||
} else {
|
||||
this._onShowDialog();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* KeyPress handler for accessibility.
|
||||
|
@ -301,7 +332,7 @@ class Popover extends Component<Props, State> {
|
|||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
_onKeyPress(e) {
|
||||
_onKeyPress(e: React.KeyboardEvent) {
|
||||
if (e.key === ' ' || e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
if (this.props.visible) {
|
||||
|
@ -312,8 +343,6 @@ class Popover extends Component<Props, State> {
|
|||
}
|
||||
}
|
||||
|
||||
_onEscKey: (Object) => void;
|
||||
|
||||
/**
|
||||
* KeyPress handler for accessibility.
|
||||
*
|
||||
|
@ -321,7 +350,7 @@ class Popover extends Component<Props, State> {
|
|||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
_onEscKey(e) {
|
||||
_onEscKey(e: React.KeyboardEvent) {
|
||||
if (e.key === 'Escape') {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
@ -331,8 +360,6 @@ class Popover extends Component<Props, State> {
|
|||
}
|
||||
}
|
||||
|
||||
_getCustomDialogStyle: (DOMRectReadOnly) => void;
|
||||
|
||||
/**
|
||||
* Gets style for positioning the context menu on screen in regards to the trigger's
|
||||
* position.
|
||||
|
@ -341,8 +368,8 @@ class Popover extends Component<Props, State> {
|
|||
*
|
||||
* @returns {Object} - The new style of the context menu.
|
||||
*/
|
||||
_getCustomDialogStyle(size) {
|
||||
if (this._containerRef && this._containerRef.current) {
|
||||
_getCustomDialogStyle(size: DOMRectReadOnly) {
|
||||
if (this._containerRef?.current) {
|
||||
const bounds = this._containerRef.current.getBoundingClientRect();
|
||||
|
||||
return getContextMenuStyle(bounds, size, this.props.position);
|
||||
|
@ -385,7 +412,7 @@ class Popover extends Component<Props, State> {
|
|||
* @private
|
||||
* @returns {Props}
|
||||
*/
|
||||
function _mapStateToProps(state) {
|
||||
function _mapStateToProps(state: IReduxState) {
|
||||
return {
|
||||
overflowDrawer: state['features/toolbox'].overflowDrawer
|
||||
};
|
|
@ -1 +0,0 @@
|
|||
export { default as Popover } from './Popover';
|
|
@ -3,7 +3,7 @@
|
|||
import React from 'react';
|
||||
|
||||
import { Icon } from '../../../icons';
|
||||
import { Popover } from '../../../popover';
|
||||
import Popover from '../../../popover/components/Popover.web';
|
||||
|
||||
type Props = {
|
||||
|
||||
|
|
|
@ -16,8 +16,7 @@ import {
|
|||
getParticipantById,
|
||||
isScreenShareParticipant
|
||||
} from '../../../base/participants/functions';
|
||||
// @ts-ignore
|
||||
import { Popover } from '../../../base/popover';
|
||||
import Popover from '../../../base/popover/components/Popover.web';
|
||||
import {
|
||||
getSourceNameByParticipantId,
|
||||
getTrackByMediaTypeAndParticipant,
|
||||
|
@ -253,7 +252,6 @@ class ConnectionIndicator extends AbstractConnectionIndicator<Props, State> {
|
|||
participantId = { participantId } /> }
|
||||
disablePopover = { !enableStatsDisplay }
|
||||
id = 'participant-connection-indicator'
|
||||
noPaddingContent = { true }
|
||||
onPopoverClose = { this._onHidePopover }
|
||||
onPopoverOpen = { this._onShowPopover }
|
||||
position = { statsPopoverPosition }
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
/* eslint-disable lines-around-comment */
|
||||
import InlineDialog from '@atlaskit/inline-dialog';
|
||||
import React, { ReactNode, useCallback } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { makeStyles } from 'tss-react/mui';
|
||||
|
@ -7,6 +6,7 @@ import { makeStyles } from 'tss-react/mui';
|
|||
import { createToolbarEvent } from '../../../analytics/AnalyticsEvents';
|
||||
import { sendAnalytics } from '../../../analytics/functions';
|
||||
import { IReduxState } from '../../../app/types';
|
||||
import Popover from '../../../base/popover/components/Popover.web';
|
||||
// @ts-ignore
|
||||
import { ReactionEmoji, ReactionsMenu } from '../../../reactions/components';
|
||||
import { REACTIONS_MENU_HEIGHT } from '../../../reactions/constants';
|
||||
|
@ -74,6 +74,10 @@ const OverflowMenuButton = ({
|
|||
onVisibilityChange(false);
|
||||
}, [ onVisibilityChange ]);
|
||||
|
||||
const onOpenDialog = useCallback(() => {
|
||||
onVisibilityChange(true);
|
||||
}, [ onVisibilityChange ]);
|
||||
|
||||
const onEscClick = useCallback((event: React.KeyboardEvent) => {
|
||||
if (event.key === 'Escape' && isOpen) {
|
||||
event.preventDefault();
|
||||
|
@ -118,16 +122,17 @@ const OverflowMenuButton = ({
|
|||
</JitsiPortal>
|
||||
</>
|
||||
) : (
|
||||
<InlineDialog
|
||||
<Popover
|
||||
content = { children }
|
||||
isOpen = { isOpen }
|
||||
onClose = { onCloseDialog }
|
||||
placement = 'top-end'>
|
||||
onPopoverClose = { onCloseDialog }
|
||||
onPopoverOpen = { onOpenDialog }
|
||||
position = 'top'
|
||||
trigger = 'click'
|
||||
visible = { isOpen }>
|
||||
<OverflowToggleButton
|
||||
handleClick = { toggleDialogVisibility }
|
||||
isOpen = { isOpen }
|
||||
onKeyDown = { onEscClick } />
|
||||
</InlineDialog>
|
||||
</Popover>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
|
|
|
@ -356,7 +356,8 @@ const styles = () => {
|
|||
position: 'relative' as const,
|
||||
right: 'auto',
|
||||
maxHeight: 'inherit',
|
||||
margin: 0
|
||||
margin: 0,
|
||||
marginBottom: '8px'
|
||||
},
|
||||
|
||||
hangupMenu: {
|
||||
|
|
|
@ -9,8 +9,7 @@ import { isMobileBrowser } from '../../../base/environment/utils';
|
|||
import { translate } from '../../../base/i18n/functions';
|
||||
import { IconHorizontalPoints } from '../../../base/icons/svg';
|
||||
import { getLocalParticipant } from '../../../base/participants/functions';
|
||||
// @ts-ignore
|
||||
import { Popover } from '../../../base/popover';
|
||||
import Popover from '../../../base/popover/components/Popover.web';
|
||||
import { setParticipantContextMenuOpen } from '../../../base/responsive-ui/actions';
|
||||
import { getHideSelfView } from '../../../base/settings/functions.web';
|
||||
import { getLocalVideoTrack } from '../../../base/tracks/functions';
|
||||
|
|
|
@ -10,8 +10,7 @@ import { translate } from '../../../base/i18n/functions';
|
|||
import { IconHorizontalPoints } from '../../../base/icons/svg';
|
||||
import { getLocalParticipant, getParticipantById } from '../../../base/participants/functions';
|
||||
import { IParticipant } from '../../../base/participants/types';
|
||||
// @ts-ignore
|
||||
import { Popover } from '../../../base/popover';
|
||||
import Popover from '../../../base/popover/components/Popover.web';
|
||||
import { setParticipantContextMenuOpen } from '../../../base/responsive-ui/actions';
|
||||
import Button from '../../../base/ui/components/web/Button';
|
||||
import ConnectionIndicatorContent from
|
||||
|
|
Loading…
Reference in New Issue