From faec46dd175759d6930adf3a9dbc6c9d2e4c60c9 Mon Sep 17 00:00:00 2001 From: Robert Pintilii Date: Mon, 24 Oct 2022 12:51:18 +0300 Subject: [PATCH] ref(popover) Update Popover to support click trigger (#12447) Convert Popover to TS Update OverflowMenu to use Popover instead of InlineDialog --- .../{Popover.web.js => Popover.web.tsx} | 131 +++++++++++------- .../features/base/popover/components/index.js | 1 - .../web/ToolboxButtonWithIconPopup.js | 2 +- .../components/web/ConnectionIndicator.tsx | 4 +- .../components/web/OverflowMenuButton.tsx | 19 ++- .../toolbox/components/web/Toolbox.tsx | 3 +- .../web/LocalVideoMenuTriggerButton.tsx | 3 +- .../web/RemoteVideoMenuTriggerButton.tsx | 3 +- 8 files changed, 97 insertions(+), 69 deletions(-) rename react/features/base/popover/components/{Popover.web.js => Popover.web.tsx} (77%) delete mode 100644 react/features/base/popover/components/index.js diff --git a/react/features/base/popover/components/Popover.web.js b/react/features/base/popover/components/Popover.web.tsx similarity index 77% rename from react/features/base/popover/components/Popover.web.js rename to react/features/base/popover/components/Popover.web.tsx index 2990c268d..ffa6bbed4 100644 --- a/react/features/base/popover/components/Popover.web.js +++ b/react/features/base/popover/components/Popover.web.tsx @@ -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 { */ static defaultProps = { className: '', - id: '' + id: '', + trigger: 'hover' }; /** * Reference to the dialog container. */ - _containerRef: Object; + _containerRef: React.RefObject; _contextMenuRef: HTMLElement; @@ -119,11 +132,12 @@ class Popover extends Component { 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 { */ 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 { */ 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 { * @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 {
{ visible && ( { ); } - _setContextMenuStyle: (size: Object) => void; - /** * Sets the context menu dialog style for positioning it on screen. * @@ -204,14 +238,12 @@ class Popover extends Component { * * @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 { * * @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 { * @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 { } } - _onShowDialog: (Object) => void; - /** * Displays the {@code InlineDialog} and calls any registered onPopoverOpen * callbacks. @@ -270,16 +296,14 @@ class Popover extends Component { * @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 { * @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 { * * @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 { } } - _onEscKey: (Object) => void; - /** * KeyPress handler for accessibility. * @@ -321,7 +350,7 @@ class Popover extends Component { * * @returns {void} */ - _onEscKey(e) { + _onEscKey(e: React.KeyboardEvent) { if (e.key === 'Escape') { e.preventDefault(); e.stopPropagation(); @@ -331,8 +360,6 @@ class Popover extends Component { } } - _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 { * * @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 { * @private * @returns {Props} */ -function _mapStateToProps(state) { +function _mapStateToProps(state: IReduxState) { return { overflowDrawer: state['features/toolbox'].overflowDrawer }; diff --git a/react/features/base/popover/components/index.js b/react/features/base/popover/components/index.js deleted file mode 100644 index 02df6c38e..000000000 --- a/react/features/base/popover/components/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default as Popover } from './Popover'; diff --git a/react/features/base/toolbox/components/web/ToolboxButtonWithIconPopup.js b/react/features/base/toolbox/components/web/ToolboxButtonWithIconPopup.js index 005c2316e..f5427f715 100644 --- a/react/features/base/toolbox/components/web/ToolboxButtonWithIconPopup.js +++ b/react/features/base/toolbox/components/web/ToolboxButtonWithIconPopup.js @@ -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 = { diff --git a/react/features/connection-indicator/components/web/ConnectionIndicator.tsx b/react/features/connection-indicator/components/web/ConnectionIndicator.tsx index b2c8ff0ab..49edbc533 100644 --- a/react/features/connection-indicator/components/web/ConnectionIndicator.tsx +++ b/react/features/connection-indicator/components/web/ConnectionIndicator.tsx @@ -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 { participantId = { participantId } /> } disablePopover = { !enableStatsDisplay } id = 'participant-connection-indicator' - noPaddingContent = { true } onPopoverClose = { this._onHidePopover } onPopoverOpen = { this._onShowPopover } position = { statsPopoverPosition } diff --git a/react/features/toolbox/components/web/OverflowMenuButton.tsx b/react/features/toolbox/components/web/OverflowMenuButton.tsx index 3639dad4d..8a81655eb 100644 --- a/react/features/toolbox/components/web/OverflowMenuButton.tsx +++ b/react/features/toolbox/components/web/OverflowMenuButton.tsx @@ -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 = ({ ) : ( - + onPopoverClose = { onCloseDialog } + onPopoverOpen = { onOpenDialog } + position = 'top' + trigger = 'click' + visible = { isOpen }> - + ) }
diff --git a/react/features/toolbox/components/web/Toolbox.tsx b/react/features/toolbox/components/web/Toolbox.tsx index 4d80781ea..7f95f2e70 100644 --- a/react/features/toolbox/components/web/Toolbox.tsx +++ b/react/features/toolbox/components/web/Toolbox.tsx @@ -356,7 +356,8 @@ const styles = () => { position: 'relative' as const, right: 'auto', maxHeight: 'inherit', - margin: 0 + margin: 0, + marginBottom: '8px' }, hangupMenu: { diff --git a/react/features/video-menu/components/web/LocalVideoMenuTriggerButton.tsx b/react/features/video-menu/components/web/LocalVideoMenuTriggerButton.tsx index 2c7d62bad..d0d7a668c 100644 --- a/react/features/video-menu/components/web/LocalVideoMenuTriggerButton.tsx +++ b/react/features/video-menu/components/web/LocalVideoMenuTriggerButton.tsx @@ -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'; diff --git a/react/features/video-menu/components/web/RemoteVideoMenuTriggerButton.tsx b/react/features/video-menu/components/web/RemoteVideoMenuTriggerButton.tsx index 39d54bc0b..2a08922b0 100644 --- a/react/features/video-menu/components/web/RemoteVideoMenuTriggerButton.tsx +++ b/react/features/video-menu/components/web/RemoteVideoMenuTriggerButton.tsx @@ -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