diff --git a/css/_audio-preview.scss b/css/_audio-preview.scss
index 293bc48dc..4ece182b3 100644
--- a/css/_audio-preview.scss
+++ b/css/_audio-preview.scss
@@ -22,11 +22,7 @@
}
&-entry-text {
- display: inline-block;
- text-overflow: ellipsis;
max-width: 213px;
- overflow: hidden;
- white-space: nowrap;
&.left-margin {
margin-left: 36px;
diff --git a/react/features/base/ui/components/web/ContextMenuItem.tsx b/react/features/base/ui/components/web/ContextMenuItem.tsx
index 779cebabc..b29377487 100644
--- a/react/features/base/ui/components/web/ContextMenuItem.tsx
+++ b/react/features/base/ui/components/web/ContextMenuItem.tsx
@@ -5,6 +5,9 @@ import { makeStyles } from 'tss-react/mui';
import { showOverflowDrawer } from '../../../../toolbox/functions.web';
import Icon from '../../../icons/components/Icon';
import { withPixelLineHeight } from '../../../styles/functions.web';
+import { TEXT_OVERFLOW_TYPES } from '../../constants.any';
+
+import TextWithOverflow from './TextWithOverflow';
export interface IProps {
@@ -59,6 +62,11 @@ export interface IProps {
*/
onKeyPress?: (e?: React.KeyboardEvent) => void;
+ /**
+ * Overflow type for item text.
+ */
+ overflowType?: TEXT_OVERFLOW_TYPES;
+
/**
* Whether the item is marked as selected.
*/
@@ -149,6 +157,7 @@ const ContextMenuItem = ({
onClick,
onKeyDown,
onKeyPress,
+ overflowType,
selected,
testId,
text,
@@ -180,12 +189,13 @@ const ContextMenuItem = ({
size = { 20 }
src = { icon } />}
{text && (
-
+ _overflowDrawer && styles.drawerText,
+ textClassName) }
+ overflowType = { overflowType } >
{text}
-
+
)}
{children}
diff --git a/react/features/base/ui/components/web/TextWithOverflow.tsx b/react/features/base/ui/components/web/TextWithOverflow.tsx
new file mode 100644
index 000000000..a05706d99
--- /dev/null
+++ b/react/features/base/ui/components/web/TextWithOverflow.tsx
@@ -0,0 +1,74 @@
+import React, { ReactNode, useRef } from 'react';
+import { keyframes } from 'styled-components';
+import { makeStyles } from 'tss-react/mui';
+
+import { TEXT_OVERFLOW_TYPES } from '../../constants.web';
+
+interface ITextWithOverflowProps {
+ children: ReactNode;
+ className?: string;
+ overflowType?: TEXT_OVERFLOW_TYPES;
+}
+
+const useStyles = makeStyles<{ translateDiff: number; }>()((_, { translateDiff }) => {
+ return {
+ animation: {
+ '&:hover': {
+ animation: `${keyframes`
+ 0%, 20% {
+ transform: translateX(0%);
+ left: 0%;
+ }
+ 80%, 100% {
+ transform: translateX(-${translateDiff}px);
+ left: 100%;
+ }
+ `} ${Math.max(translateDiff * 50, 2000)}ms infinite alternate linear;`
+ }
+ },
+ textContainer: {
+ overflow: 'hidden'
+ },
+ [TEXT_OVERFLOW_TYPES.ELLIPSIS]: {
+ display: 'block',
+ overflow: 'hidden',
+ textOverflow: 'ellipsis',
+ whiteSpace: 'nowrap'
+ },
+ [TEXT_OVERFLOW_TYPES.SCROLL_ON_HOVER]: {
+ display: 'inline-block',
+ overflow: 'visible',
+ whiteSpace: 'nowrap'
+ }
+ };
+});
+
+const TextWithOverflow = ({
+ className,
+ overflowType = TEXT_OVERFLOW_TYPES.ELLIPSIS,
+ children
+}: ITextWithOverflowProps) => {
+ const containerRef = useRef(null);
+ const contentRef = useRef(null);
+ const shouldAnimateOnHover = overflowType === TEXT_OVERFLOW_TYPES.SCROLL_ON_HOVER
+ && containerRef.current
+ && contentRef.current
+ && containerRef.current.clientWidth < contentRef.current.clientWidth;
+
+ const translateDiff = shouldAnimateOnHover ? contentRef.current.clientWidth - containerRef.current.clientWidth : 0;
+ const { classes: styles, cx } = useStyles({ translateDiff });
+
+ return (
+
+
+ {children}
+
+
+ );
+};
+
+export default TextWithOverflow;
diff --git a/react/features/base/ui/constants.any.ts b/react/features/base/ui/constants.any.ts
index 8e634954b..26a75e8ee 100644
--- a/react/features/base/ui/constants.any.ts
+++ b/react/features/base/ui/constants.any.ts
@@ -8,6 +8,14 @@ export enum BUTTON_TYPES {
TERTIARY = 'tertiary'
}
+/**
+ * Behaviour types for showing overflow text content.
+ */
+export enum TEXT_OVERFLOW_TYPES {
+ ELLIPSIS = 'ellipsis',
+ SCROLL_ON_HOVER = 'scroll-on-hover'
+}
+
/**
* The modes of the buttons.
*/
diff --git a/react/features/settings/components/web/audio/MicrophoneEntry.tsx b/react/features/settings/components/web/audio/MicrophoneEntry.tsx
index 7403ace9c..789865e17 100644
--- a/react/features/settings/components/web/audio/MicrophoneEntry.tsx
+++ b/react/features/settings/components/web/audio/MicrophoneEntry.tsx
@@ -7,6 +7,7 @@ import { IconCheck, IconExclamationSolid } from '../../../../base/icons/svg';
// @ts-ignore
import JitsiMeetJS from '../../../../base/lib-jitsi-meet/_';
import ContextMenuItem from '../../../../base/ui/components/web/ContextMenuItem';
+import { TEXT_OVERFLOW_TYPES } from '../../../../base/ui/constants.any';
import Meter from './Meter';
@@ -231,6 +232,7 @@ export default class MicrophoneEntry extends Component {
diff --git a/react/features/settings/components/web/audio/SpeakerEntry.tsx b/react/features/settings/components/web/audio/SpeakerEntry.tsx
index 3d0965ce0..af9daa193 100644
--- a/react/features/settings/components/web/audio/SpeakerEntry.tsx
+++ b/react/features/settings/components/web/audio/SpeakerEntry.tsx
@@ -4,7 +4,7 @@ import React, { useRef } from 'react';
import { IconCheck } from '../../../../base/icons/svg';
import Button from '../../../../base/ui/components/web/Button';
import ContextMenuItem from '../../../../base/ui/components/web/ContextMenuItem';
-import { BUTTON_TYPES } from '../../../../base/ui/constants.any';
+import { BUTTON_TYPES, TEXT_OVERFLOW_TYPES } from '../../../../base/ui/constants.any';
import logger from '../../../logger';
const TEST_SOUND_PATH = 'sounds/ring.mp3';
@@ -118,6 +118,7 @@ const SpeakerEntry = (props: IProps) => {