diff --git a/css/_atlaskit_overrides.scss b/css/_atlaskit_overrides.scss index 5cd48e7f3..00a5e2dfe 100644 --- a/css/_atlaskit_overrides.scss +++ b/css/_atlaskit_overrides.scss @@ -46,18 +46,12 @@ } .audio-preview > div:nth-child(2), -.video-preview > div:nth-child(2), -.reactions-menu-popup > div:nth-child(2) { +.video-preview > div:nth-child(2) { margin-bottom: 4px; outline: none; padding: 0; } -.reactions-menu-popup > div:nth-child(2) { - margin-bottom: 6px; - box-shadow: none; -} - /** * The following selectors keep the chat modal full-size anywhere between 100px * and 580px for desktop or 680px for mobile. diff --git a/css/_reactions-menu.scss b/css/_reactions-menu.scss index 2534ebff6..46b2293bf 100644 --- a/css/_reactions-menu.scss +++ b/css/_reactions-menu.scss @@ -104,6 +104,10 @@ } } +.reactions-menu-container { + padding-bottom: 6px; +} + .reactions-animations-container { position: absolute; width: 20%; @@ -112,8 +116,7 @@ height: 0; } -.reactions-menu-popup-container, -.reactions-menu-popup { +.reactions-menu-popup-container { display: inline-block; position: relative; } diff --git a/css/_settings-button.scss b/css/_settings-button.scss index 83a96e30b..a99ab1923 100644 --- a/css/_settings-button.scss +++ b/css/_settings-button.scss @@ -60,3 +60,15 @@ } } } + +.settings-button-small-icon-container { + position: absolute; + right: -4px; + top: -3px; + + & .settings-button-small-icon { + position: relative; + top: 0; + right: 0; + } +} diff --git a/react/features/base/toolbox/components/web/ToolboxButtonWithIconPopup.js b/react/features/base/toolbox/components/web/ToolboxButtonWithIconPopup.js new file mode 100644 index 000000000..005c2316e --- /dev/null +++ b/react/features/base/toolbox/components/web/ToolboxButtonWithIconPopup.js @@ -0,0 +1,138 @@ +// @flow + +import React from 'react'; + +import { Icon } from '../../../icons'; +import { Popover } from '../../../popover'; + +type Props = { + + /** + * Whether the element popup is expanded. + */ + ariaExpanded?: boolean, + + /** + * The id of the element this button icon controls. + */ + ariaControls?: string, + + /** + * Whether the element has a popup. + */ + ariaHasPopup?: boolean, + + /** + * Aria label for the Icon. + */ + ariaLabel?: string, + + /** + * The decorated component (ToolboxButton). + */ + children: React$Node, + + /** + * Icon of the button. + */ + icon: Function, + + /** + * Flag used for disabling the small icon. + */ + iconDisabled: boolean, + + /** + * The ID of the icon button. + */ + iconId: string, + + /** + * Popover close callback. + */ + onPopoverClose: Function, + + /** + * Popover open callback. + */ + onPopoverOpen: Function, + + /** + * The content that will be displayed inside the popover. + */ + popoverContent: React$Node, + + /** + * Additional styles. + */ + styles?: Object, + + /** + * Whether or not the popover is visible. + */ + visible: boolean +}; + +declare var APP: Object; + +/** + * Displays the `ToolboxButtonWithIcon` component. + * + * @param {Object} props - Component's props. + * @returns {ReactElement} + */ +export default function ToolboxButtonWithIconPopup(props: Props) { + const { + ariaControls, + ariaExpanded, + ariaHasPopup, + ariaLabel, + children, + icon, + iconDisabled, + iconId, + onPopoverClose, + onPopoverOpen, + popoverContent, + styles, + visible + } = props; + + const iconProps = {}; + + if (iconDisabled) { + iconProps.className + = 'settings-button-small-icon settings-button-small-icon--disabled'; + } else { + iconProps.className = 'settings-button-small-icon'; + iconProps.role = 'button'; + iconProps.tabIndex = 0; + iconProps.ariaControls = ariaControls; + iconProps.ariaExpanded = ariaExpanded; + iconProps.containerId = iconId; + } + + + return ( +
+ {children} +
+ + + +
+
+ ); +} diff --git a/react/features/reactions/components/web/ReactionsMenuButton.js b/react/features/reactions/components/web/ReactionsMenuButton.js index 24623cc4a..a0a05da67 100644 --- a/react/features/reactions/components/web/ReactionsMenuButton.js +++ b/react/features/reactions/components/web/ReactionsMenuButton.js @@ -1,12 +1,13 @@ // @flow import React, { useCallback } from 'react'; +import { useSelector } from 'react-redux'; import { isMobileBrowser } from '../../../base/environment/utils'; import { translate } from '../../../base/i18n'; import { IconArrowUp } from '../../../base/icons'; import { connect } from '../../../base/redux'; -import { ToolboxButtonWithIcon } from '../../../base/toolbox/components'; +import ToolboxButtonWithIconPopup from '../../../base/toolbox/components/web/ToolboxButtonWithIconPopup'; import { toggleReactionsMenuVisibility } from '../../actions.web'; import { type ReactionEmojiProps } from '../../constants'; import { getReactionsQueue, isReactionsEnabled } from '../../functions.any'; @@ -14,7 +15,7 @@ import { getReactionsMenuVisibility } from '../../functions.web'; import RaiseHandButton from './RaiseHandButton'; import ReactionEmoji from './ReactionEmoji'; -import ReactionsMenuPopup from './ReactionsMenuPopup'; +import ReactionsMenu from './ReactionsMenu'; type Props = { @@ -26,7 +27,7 @@ type Props = { /** * The button's key. */ - buttonKey?: string, + buttonKey?: string, /** * Redux dispatch function. @@ -84,38 +85,47 @@ function ReactionsMenuButton({ reactionsQueue, t }: Props) { + const visible = useSelector(getReactionsMenuVisibility); const toggleReactionsMenu = useCallback(() => { dispatch(toggleReactionsMenuVisibility()); }, [ dispatch ]); + const openReactionsMenu = useCallback(() => { + !visible && toggleReactionsMenu(); + }, [ visible, toggleReactionsMenu ]); + + const reactionsMenu = (
+ +
); + return (
- - {!_reactionsEnabled || isMobile ? ( - ) + : ( + ) - : ( - + - - - )} - + handleClick = { handleClick } + notifyMode = { notifyMode } /> + + )} {reactionsQueue.map(({ reaction, uid }, index) => ( getReactionsMenuVisibility(state)); - - const dispatch = useDispatch(); - const onClose = useCallback(() => { - dispatch(toggleReactionsMenuVisibility()); - }); - - return ( -
- } - isOpen = { isOpen } - onClose = { onClose } - placement = 'top'> - {children} - -
- ); -} - -export default ReactionsMenuPopup; diff --git a/react/features/reactions/components/web/index.js b/react/features/reactions/components/web/index.js index c63636541..86ac96a8c 100644 --- a/react/features/reactions/components/web/index.js +++ b/react/features/reactions/components/web/index.js @@ -4,4 +4,3 @@ export { default as ReactionButton } from './ReactionButton'; export { default as ReactionEmoji } from './ReactionEmoji'; export { default as ReactionsMenu } from './ReactionsMenu'; export { default as ReactionsMenuButton } from './ReactionsMenuButton'; -export { default as ReactionsMenuPopup } from './ReactionsMenuPopup'; diff --git a/react/features/toolbox/components/web/DialogPortal.js b/react/features/toolbox/components/web/DialogPortal.js index feb779bd4..1189ba49c 100644 --- a/react/features/toolbox/components/web/DialogPortal.js +++ b/react/features/toolbox/components/web/DialogPortal.js @@ -1,6 +1,6 @@ // @flow -import { useEffect, useState } from 'react'; +import { useEffect, useRef, useState } from 'react'; import ReactDOM from 'react-dom'; type Props = { @@ -41,8 +41,11 @@ function DialogPortal({ children, className, style, getRef, setSize }: Props) { const [ portalTarget ] = useState(() => { const portalDiv = document.createElement('div'); + portalDiv.style.visibility = 'hidden'; + return portalDiv; }); + const timerRef = useRef(); useEffect(() => { if (style) { @@ -74,6 +77,10 @@ function DialogPortal({ children, className, style, getRef, setSize }: Props) { if (contentRect.width !== size.width || contentRect.height !== size.height) { setSize && setSize(contentRect); + clearTimeout(timerRef.current); + timerRef.current = setTimeout(() => { + portalTarget.style.visibility = 'visible'; + }, 100); } });