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 (
+
+ );
+}
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);
}
});