diff --git a/css/_toolbars.scss b/css/_toolbars.scss
index 18d8314e72..e771f3d67f 100644
--- a/css/_toolbars.scss
+++ b/css/_toolbars.scss
@@ -32,6 +32,14 @@
pointer-events: none;
z-index: $toolbarZ + 2;
+ &.shift-up {
+ bottom: calc(((#{$newToolbarSize} + 30px) * 2) * -1);
+
+ .toolbox-content {
+ margin-bottom: 46px;
+ }
+ }
+
&.visible {
bottom: 0;
}
diff --git a/react/features/filmstrip/components/web/ThumbnailBottomIndicators.tsx b/react/features/filmstrip/components/web/ThumbnailBottomIndicators.tsx
index 2dcded8faa..289ca8dc39 100644
--- a/react/features/filmstrip/components/web/ThumbnailBottomIndicators.tsx
+++ b/react/features/filmstrip/components/web/ThumbnailBottomIndicators.tsx
@@ -61,7 +61,7 @@ const ThumbnailBottomIndicators = ({
showStatusIndicators = true,
thumbnailType
}: IProps) => {
- const { classes: styles } = useStyles();
+ const { classes: styles, cx } = useStyles();
const _allowEditing = !useSelector(isNameReadOnly);
const _defaultLocalDisplayName = interfaceConfig.DEFAULT_LOCAL_DISPLAY_NAME;
const _showDisplayName = useSelector(isDisplayNameVisible);
@@ -69,7 +69,7 @@ const ThumbnailBottomIndicators = ({
(state: IReduxState) => isScreenShareParticipantById(state, participantId)
);
- return (
+ return (
{
showStatusIndicators &&
{
return (
{ this._renderToolboxContent() }
@@ -1614,6 +1620,7 @@ function _mapStateToProps(state: IReduxState, ownProps: any) {
_reactionsButtonEnabled: isReactionsButtonEnabled(state),
_reactionsEnabled,
_screenSharing: isScreenVideoShared(state),
+ _shiftUp: state['features/toolbox'].shiftUp,
_shortcutsEnabled: areKeyboardShortcutsEnabled(state),
_tileViewEnabled: shouldDisplayTileView(state),
_toolbarButtons: toolbarButtons,
diff --git a/react/features/toolbox/reducer.ts b/react/features/toolbox/reducer.ts
index ed85409b95..2b106458e4 100644
--- a/react/features/toolbox/reducer.ts
+++ b/react/features/toolbox/reducer.ts
@@ -9,6 +9,7 @@ import {
SET_OVERFLOW_MENU_VISIBLE,
SET_TOOLBAR_HOVERED,
SET_TOOLBOX_ENABLED,
+ SET_TOOLBOX_SHIFT_UP,
SET_TOOLBOX_TIMEOUT,
SET_TOOLBOX_VISIBLE,
TOGGLE_TOOLBOX_VISIBLE
@@ -55,6 +56,11 @@ const INITIAL_STATE = {
*/
overflowMenuVisible: false,
+ /**
+ * Whether to shift the toolbar up (in case it overlaps the tiles names).
+ */
+ shiftUp: false,
+
/**
* A number, non-zero value which identifies the timer created by a call
* to setTimeout().
@@ -79,6 +85,7 @@ export interface IToolboxState {
hovered: boolean;
overflowDrawer: boolean;
overflowMenuVisible: boolean;
+ shiftUp: boolean;
timeoutID?: number | null;
visible: boolean;
}
@@ -135,6 +142,12 @@ ReducerRegistry.register(
timeoutID: action.timeoutID
};
+ case SET_TOOLBOX_SHIFT_UP:
+ return {
+ ...state,
+ shiftUp: action.shiftUp
+ };
+
case SET_TOOLBOX_VISIBLE:
return set(state, 'visible', action.visible);
diff --git a/react/features/toolbox/subscriber.ts b/react/features/toolbox/subscriber.ts
index 18504638d3..95c0f7b028 100644
--- a/react/features/toolbox/subscriber.ts
+++ b/react/features/toolbox/subscriber.ts
@@ -1,6 +1,12 @@
+import { throttle } from 'lodash';
+
import { IReduxState, IStore } from '../app/types';
+import { getParticipantCount } from '../base/participants/functions';
import StateListenerRegistry from '../base/redux/StateListenerRegistry';
+import { DEFAULT_MAX_COLUMNS } from '../filmstrip/constants';
+import { isLayoutTileView } from '../video-layout/functions.any';
+import { setShiftUp } from './actions.any';
import { isAudioMuteButtonDisabled } from './functions.any';
/**
@@ -18,3 +24,84 @@ StateListenerRegistry.register(
}
}
);
+
+
+const checkToolboxOverlap = (clientHeight: number, store: IStore) => {
+ let toolboxRect = document.querySelector('.toolbox-content-items')?.getBoundingClientRect();
+
+ if (!toolboxRect) {
+ return;
+ }
+ const tiles = document.querySelectorAll('span.videocontainer');
+
+ if (!tiles.length) {
+ return;
+ }
+
+ const toolboxHeight = 48 + 12; // height + padding
+ const bottomMargin = 16;
+
+ // Set top and bottom manually to avoid wrong coordinates
+ // caused by the hiding/ showing of the toolbox.
+ toolboxRect = {
+ ...toolboxRect,
+ top: clientHeight - toolboxHeight - bottomMargin,
+ bottom: clientHeight - bottomMargin,
+ left: toolboxRect.left,
+ right: toolboxRect.right
+ };
+ let isIntersecting = false;
+
+ const rows = store.getState()['features/filmstrip'].tileViewDimensions?.gridDimensions?.rows;
+ const noOfTilesToCheck = rows === 1 ? tiles.length : DEFAULT_MAX_COLUMNS - 1;
+
+ for (let i = 1; i < Math.max(noOfTilesToCheck, tiles.length); i++) {
+ const tile = tiles[tiles.length - i];
+ const indicatorsRect = tile?.querySelector('.bottom-indicators')?.getBoundingClientRect();
+
+ if (!indicatorsRect) {
+ // eslint-disable-next-line no-continue
+ continue;
+ }
+
+ if (indicatorsRect.top <= toolboxRect.bottom
+ && indicatorsRect.right >= toolboxRect.left
+ && indicatorsRect.bottom >= toolboxRect.top
+ && indicatorsRect.left <= toolboxRect.right
+ ) {
+ isIntersecting = true;
+ break;
+ }
+ }
+
+ store.dispatch(setShiftUp(isIntersecting));
+};
+
+const throttledCheckOverlap = throttle(checkToolboxOverlap, 100, {
+ leading: false,
+ trailing: true
+});
+
+/**
+ * Listens for changes in the selected layout to calculate the dimensions of the tile view grid and horizontal view.
+ */
+StateListenerRegistry.register(
+ /* selector */ state => {
+ const { clientHeight, clientWidth } = state['features/base/responsive-ui'];
+
+ return {
+ participantCount: getParticipantCount(state),
+ clientHeight,
+ clientWidth,
+ isTileView: isLayoutTileView(state)
+ };
+ },
+ /* listener */({ clientHeight, isTileView }, store) => {
+ if (!isTileView) {
+ return;
+ }
+ throttledCheckOverlap(clientHeight, store);
+
+ }, {
+ deepEquals: true
+ });