mirror of
https://gitcode.com/GitHub_Trending/ji/jitsi-meet.git
synced 2025-12-30 03:12:29 +00:00
feat(tooltip) Create and move to our component (#13061)
Create Tooltip component Fix Popover positioning calculations Add margins to popover Remove @atlaskit/tooltip Update all components to use the new Tooltip component Added tooltip actions and reducers for the following functionality: when a user hovers over an element is sees the tooltip for that element and then hovers another element that has a tooltip, instead of using the delay and animations we just unmount the current tooltip and mount the next one immediately
This commit is contained in:
@@ -1,49 +1,27 @@
|
|||||||
/**
|
|
||||||
* Mousemove padding styles are used to add invisible elements to the popover
|
|
||||||
* to allow mouse movement from the popover trigger to the popover itself
|
|
||||||
* without triggering a mouseleave event.
|
|
||||||
*/
|
|
||||||
%vertical-popover-padding {
|
|
||||||
height: 100%;
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
width: 20px;
|
|
||||||
padding: 20px 0;
|
|
||||||
top: -20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
%horizontal-popover-padding {
|
|
||||||
height: 25px;
|
|
||||||
position: absolute;
|
|
||||||
right: 0;
|
|
||||||
width: 100%;
|
|
||||||
padding: 0 35px;
|
|
||||||
left: -35px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.popover-mousemove-padding-left {
|
|
||||||
@extend %vertical-popover-padding;
|
|
||||||
left: -35px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.popover-mousemove-padding-right {
|
|
||||||
@extend %vertical-popover-padding;
|
|
||||||
right: -35px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.popover-mousemove-padding-bottom {
|
|
||||||
@extend %horizontal-popover-padding;
|
|
||||||
bottom: -40px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.popover-mousemove-padding-top {
|
|
||||||
@extend %horizontal-popover-padding;
|
|
||||||
top: -40px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.popover {
|
.popover {
|
||||||
margin: -16px -24px;
|
margin: -16px -24px;
|
||||||
z-index: $popoverZ;
|
z-index: $popoverZ;
|
||||||
|
|
||||||
|
.popover-content {
|
||||||
|
margin: 16px 24px;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&.top {
|
||||||
|
bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.bottom {
|
||||||
|
top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.left {
|
||||||
|
right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.right {
|
||||||
|
left: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.excalidraw .popover {
|
.excalidraw .popover {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
width: 280px;
|
width: 280px;
|
||||||
background: $menuBG;
|
background: $menuBG;
|
||||||
box-shadow: 0px 3px 16px rgba(0, 0, 0, 0.6), 0px 0px 4px 1px rgba(0, 0, 0, 0.25);
|
box-shadow: 0px 3px 16px rgba(0, 0, 0, 0.6), 0px 0px 4px 1px rgba(0, 0, 0, 0.25);
|
||||||
border-radius: 3px;
|
border-radius: 6px;
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
|
|
||||||
&.with-gif {
|
&.with-gif {
|
||||||
@@ -104,10 +104,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.reactions-menu-container {
|
|
||||||
padding-bottom: 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.reactions-animations-container {
|
.reactions-animations-container {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
width: 20%;
|
width: 20%;
|
||||||
|
|||||||
@@ -34,20 +34,20 @@
|
|||||||
background: #F2F3F4;
|
background: #F2F3F4;
|
||||||
box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25), 0px 0px 0px 1px rgba(0, 0, 0, 0.1);
|
box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25), 0px 0px 0px 1px rgba(0, 0, 0, 0.1);
|
||||||
|
|
||||||
& > svg {
|
& svg {
|
||||||
fill: #040404;
|
fill: #040404;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.settings-button-small-icon--disabled {
|
&.settings-button-small-icon--disabled {
|
||||||
background: #36383C;
|
background: #36383C;
|
||||||
|
|
||||||
&> svg {
|
& svg {
|
||||||
fill: #929292;
|
fill: #929292;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
& > svg {
|
& svg {
|
||||||
fill: #fff;
|
fill: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,7 +55,7 @@
|
|||||||
background-color: #36383c;
|
background-color: #36383c;
|
||||||
cursor: default;
|
cursor: default;
|
||||||
|
|
||||||
&> svg {
|
& svg {
|
||||||
fill: #929292;
|
fill: #929292;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
56
package-lock.json
generated
56
package-lock.json
generated
@@ -15,7 +15,6 @@
|
|||||||
"@atlaskit/inline-message": "11.0.8",
|
"@atlaskit/inline-message": "11.0.8",
|
||||||
"@atlaskit/multi-select": "15.0.5",
|
"@atlaskit/multi-select": "15.0.5",
|
||||||
"@atlaskit/theme": "11.0.2",
|
"@atlaskit/theme": "11.0.2",
|
||||||
"@atlaskit/tooltip": "17.1.2",
|
|
||||||
"@emotion/react": "11.10.0",
|
"@emotion/react": "11.10.0",
|
||||||
"@emotion/styled": "11.10.0",
|
"@emotion/styled": "11.10.0",
|
||||||
"@giphy/js-fetch-api": "4.7.1",
|
"@giphy/js-fetch-api": "4.7.1",
|
||||||
@@ -599,18 +598,6 @@
|
|||||||
"styled-components": "^3.2.6"
|
"styled-components": "^3.2.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@atlaskit/motion": {
|
|
||||||
"version": "0.4.8",
|
|
||||||
"resolved": "https://registry.npmjs.org/@atlaskit/motion/-/motion-0.4.8.tgz",
|
|
||||||
"integrity": "sha512-+5hABjUNUgl66zNya+EmNXLrbJQ7EgXA+fNQih7qzu7MkIuzJjT5vEtpXrzckLKBu1v1HsbMQW90Wnd8MJugdw==",
|
|
||||||
"dependencies": {
|
|
||||||
"@babel/runtime": "^7.0.0",
|
|
||||||
"@emotion/core": "^10.0.9"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"react": "^16.8.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@atlaskit/multi-select": {
|
"node_modules/@atlaskit/multi-select": {
|
||||||
"version": "15.0.5",
|
"version": "15.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/@atlaskit/multi-select/-/multi-select-15.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/@atlaskit/multi-select/-/multi-select-15.0.5.tgz",
|
||||||
@@ -789,25 +776,6 @@
|
|||||||
"@babel/types": "^7.15.0"
|
"@babel/types": "^7.15.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@atlaskit/tooltip": {
|
|
||||||
"version": "17.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@atlaskit/tooltip/-/tooltip-17.1.2.tgz",
|
|
||||||
"integrity": "sha512-0N63l18ZuyL2pjNXwY3pwnNos3HjOsFhSmnj2TLASC+RLz/6/Nk1k9gXuydfMjU2ntsr7xrVY5U7ING0mxAzQQ==",
|
|
||||||
"dependencies": {
|
|
||||||
"@atlaskit/analytics-next": "^8.1.0",
|
|
||||||
"@atlaskit/motion": "^0.4.0",
|
|
||||||
"@atlaskit/popper": "^5.0.0",
|
|
||||||
"@atlaskit/portal": "^4.0.0",
|
|
||||||
"@atlaskit/theme": "^11.0.0",
|
|
||||||
"@babel/runtime": "^7.0.0",
|
|
||||||
"@emotion/core": "^10.0.9",
|
|
||||||
"bind-event-listener": "^1.0.2"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"react": "^16.8.0",
|
|
||||||
"react-dom": "^16.8.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@babel/code-frame": {
|
"node_modules/@babel/code-frame": {
|
||||||
"version": "7.18.6",
|
"version": "7.18.6",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz",
|
||||||
@@ -20705,15 +20673,6 @@
|
|||||||
"react-scrolllock": "^5.0.1"
|
"react-scrolllock": "^5.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@atlaskit/motion": {
|
|
||||||
"version": "0.4.8",
|
|
||||||
"resolved": "https://registry.npmjs.org/@atlaskit/motion/-/motion-0.4.8.tgz",
|
|
||||||
"integrity": "sha512-+5hABjUNUgl66zNya+EmNXLrbJQ7EgXA+fNQih7qzu7MkIuzJjT5vEtpXrzckLKBu1v1HsbMQW90Wnd8MJugdw==",
|
|
||||||
"requires": {
|
|
||||||
"@babel/runtime": "^7.0.0",
|
|
||||||
"@emotion/core": "^10.0.9"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@atlaskit/multi-select": {
|
"@atlaskit/multi-select": {
|
||||||
"version": "15.0.5",
|
"version": "15.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/@atlaskit/multi-select/-/multi-select-15.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/@atlaskit/multi-select/-/multi-select-15.0.5.tgz",
|
||||||
@@ -20857,21 +20816,6 @@
|
|||||||
"@babel/types": "^7.15.0"
|
"@babel/types": "^7.15.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@atlaskit/tooltip": {
|
|
||||||
"version": "17.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@atlaskit/tooltip/-/tooltip-17.1.2.tgz",
|
|
||||||
"integrity": "sha512-0N63l18ZuyL2pjNXwY3pwnNos3HjOsFhSmnj2TLASC+RLz/6/Nk1k9gXuydfMjU2ntsr7xrVY5U7ING0mxAzQQ==",
|
|
||||||
"requires": {
|
|
||||||
"@atlaskit/analytics-next": "^8.1.0",
|
|
||||||
"@atlaskit/motion": "^0.4.0",
|
|
||||||
"@atlaskit/popper": "^5.0.0",
|
|
||||||
"@atlaskit/portal": "^4.0.0",
|
|
||||||
"@atlaskit/theme": "^11.0.0",
|
|
||||||
"@babel/runtime": "^7.0.0",
|
|
||||||
"@emotion/core": "^10.0.9",
|
|
||||||
"bind-event-listener": "^1.0.2"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@babel/code-frame": {
|
"@babel/code-frame": {
|
||||||
"version": "7.18.6",
|
"version": "7.18.6",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz",
|
||||||
|
|||||||
@@ -20,7 +20,6 @@
|
|||||||
"@atlaskit/inline-message": "11.0.8",
|
"@atlaskit/inline-message": "11.0.8",
|
||||||
"@atlaskit/multi-select": "15.0.5",
|
"@atlaskit/multi-select": "15.0.5",
|
||||||
"@atlaskit/theme": "11.0.2",
|
"@atlaskit/theme": "11.0.2",
|
||||||
"@atlaskit/tooltip": "17.1.2",
|
|
||||||
"@emotion/react": "11.10.0",
|
"@emotion/react": "11.10.0",
|
||||||
"@emotion/styled": "11.10.0",
|
"@emotion/styled": "11.10.0",
|
||||||
"@giphy/js-fetch-api": "4.7.1",
|
"@giphy/js-fetch-api": "4.7.1",
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
// @flow
|
|
||||||
|
|
||||||
import '../base/devices/reducer';
|
import '../base/devices/reducer';
|
||||||
|
import '../base/tooltip/reducer';
|
||||||
import '../e2ee/reducer';
|
import '../e2ee/reducer';
|
||||||
import '../face-landmarks/reducer';
|
import '../face-landmarks/reducer';
|
||||||
import '../feedback/reducer';
|
import '../feedback/reducer';
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import { IResponsiveUIState } from '../base/responsive-ui/reducer';
|
|||||||
import { ISettingsState } from '../base/settings/reducer';
|
import { ISettingsState } from '../base/settings/reducer';
|
||||||
import { ISoundsState } from '../base/sounds/reducer';
|
import { ISoundsState } from '../base/sounds/reducer';
|
||||||
import { ITestingState } from '../base/testing/reducer';
|
import { ITestingState } from '../base/testing/reducer';
|
||||||
|
import { ITooltipState } from '../base/tooltip/reducer';
|
||||||
import { INoSrcDataState, ITracksState } from '../base/tracks/reducer';
|
import { INoSrcDataState, ITracksState } from '../base/tracks/reducer';
|
||||||
import { IUserInteractionState } from '../base/user-interaction/reducer';
|
import { IUserInteractionState } from '../base/user-interaction/reducer';
|
||||||
import { IBreakoutRoomsState } from '../breakout-rooms/reducer';
|
import { IBreakoutRoomsState } from '../breakout-rooms/reducer';
|
||||||
@@ -111,6 +112,7 @@ export interface IReduxState {
|
|||||||
'features/base/responsive-ui': IResponsiveUIState;
|
'features/base/responsive-ui': IResponsiveUIState;
|
||||||
'features/base/settings': ISettingsState;
|
'features/base/settings': ISettingsState;
|
||||||
'features/base/sounds': ISoundsState;
|
'features/base/sounds': ISoundsState;
|
||||||
|
'features/base/tooltip': ITooltipState;
|
||||||
'features/base/tracks': ITracksState;
|
'features/base/tracks': ITracksState;
|
||||||
'features/base/user-interaction': IUserInteractionState;
|
'features/base/user-interaction': IUserInteractionState;
|
||||||
'features/breakout-rooms': IBreakoutRoomsState;
|
'features/breakout-rooms': IBreakoutRoomsState;
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import { IReduxState } from '../../../app/types';
|
|||||||
import DialogPortal from '../../../toolbox/components/web/DialogPortal';
|
import DialogPortal from '../../../toolbox/components/web/DialogPortal';
|
||||||
import Drawer from '../../../toolbox/components/web/Drawer';
|
import Drawer from '../../../toolbox/components/web/Drawer';
|
||||||
import JitsiPortal from '../../../toolbox/components/web/JitsiPortal';
|
import JitsiPortal from '../../../toolbox/components/web/JitsiPortal';
|
||||||
import { isMobileBrowser } from '../../environment/utils';
|
|
||||||
import { connect } from '../../redux/functions';
|
import { connect } from '../../redux/functions';
|
||||||
import { getContextMenuStyle } from '../functions.web';
|
import { getContextMenuStyle } from '../functions.web';
|
||||||
|
|
||||||
@@ -14,6 +13,11 @@ import { getContextMenuStyle } from '../functions.web';
|
|||||||
*/
|
*/
|
||||||
interface IProps {
|
interface IProps {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the child element can be clicked on.
|
||||||
|
*/
|
||||||
|
allowClick?: boolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A child React Element to use as the trigger for showing the dialog.
|
* A child React Element to use as the trigger for showing the dialog.
|
||||||
*/
|
*/
|
||||||
@@ -245,7 +249,8 @@ class Popover extends Component<IProps, IState> {
|
|||||||
<DialogPortal
|
<DialogPortal
|
||||||
getRef = { this._setContextMenuRef }
|
getRef = { this._setContextMenuRef }
|
||||||
setSize = { this._setContextMenuStyle }
|
setSize = { this._setContextMenuStyle }
|
||||||
style = { this.state.contextMenuStyle }>
|
style = { this.state.contextMenuStyle }
|
||||||
|
targetSelector = '.popover-content'>
|
||||||
<ReactFocusLock
|
<ReactFocusLock
|
||||||
lockProps = {{
|
lockProps = {{
|
||||||
role: 'dialog',
|
role: 'dialog',
|
||||||
@@ -345,9 +350,11 @@ class Popover extends Component<IProps, IState> {
|
|||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
_onClick(event: React.MouseEvent) {
|
_onClick(event: React.MouseEvent) {
|
||||||
const { trigger, visible } = this.props;
|
const { allowClick, trigger, visible } = this.props;
|
||||||
|
|
||||||
event.stopPropagation();
|
if (!allowClick) {
|
||||||
|
event.stopPropagation();
|
||||||
|
}
|
||||||
if (trigger === 'click') {
|
if (trigger === 'click') {
|
||||||
if (visible) {
|
if (visible) {
|
||||||
this._onHideDialog();
|
this._onHideDialog();
|
||||||
@@ -417,20 +424,15 @@ class Popover extends Component<IProps, IState> {
|
|||||||
* @returns {ReactElement}
|
* @returns {ReactElement}
|
||||||
*/
|
*/
|
||||||
_renderContent() {
|
_renderContent() {
|
||||||
const { content } = this.props;
|
const { content, position } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className = 'popover'
|
className = 'popover'
|
||||||
onKeyDown = { this._onEscKey }>
|
onKeyDown = { this._onEscKey }>
|
||||||
{ content }
|
<div className = { `popover-content ${position.split('-')[0]}` }>
|
||||||
{!isMobileBrowser() && (
|
{ content }
|
||||||
<>
|
</div>
|
||||||
<div className = 'popover-mousemove-padding-top' />
|
|
||||||
<div className = 'popover-mousemove-padding-right' />
|
|
||||||
<div className = 'popover-mousemove-padding-left' />
|
|
||||||
<div className = 'popover-mousemove-padding-bottom' />
|
|
||||||
</>)}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,43 +1,40 @@
|
|||||||
const LEFT_RIGHT_OFFSET = 25;
|
|
||||||
const TOP_BOTTOM_OFFSET = 20;
|
|
||||||
|
|
||||||
const getLeftAlignedStyle = (bounds: DOMRect) => {
|
const getLeftAlignedStyle = (bounds: DOMRect) => {
|
||||||
return {
|
return {
|
||||||
position: 'fixed',
|
position: 'fixed',
|
||||||
right: `${window.innerWidth - bounds.x + LEFT_RIGHT_OFFSET}px`
|
right: `${window.innerWidth - bounds.x}px`
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const getRightAlignedStyle = (bounds: DOMRect) => {
|
const getRightAlignedStyle = (bounds: DOMRect) => {
|
||||||
return {
|
return {
|
||||||
position: 'fixed',
|
position: 'fixed',
|
||||||
left: `${bounds.x + bounds.width + LEFT_RIGHT_OFFSET}px`
|
left: `${bounds.x + bounds.width}px`
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const getTopAlignedStyle = (bounds: DOMRect) => {
|
const getTopAlignedStyle = (bounds: DOMRect) => {
|
||||||
return {
|
return {
|
||||||
position: 'fixed',
|
position: 'fixed',
|
||||||
bottom: `${window.innerHeight - bounds.y + TOP_BOTTOM_OFFSET}px`
|
bottom: `${window.innerHeight - bounds.y}px`
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const getBottomAlignedStyle = (bounds: DOMRect) => {
|
const getBottomAlignedStyle = (bounds: DOMRect) => {
|
||||||
return {
|
return {
|
||||||
position: 'fixed',
|
position: 'fixed',
|
||||||
top: `${bounds.y + bounds.height + TOP_BOTTOM_OFFSET}px`
|
top: `${bounds.y + bounds.height}px`
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const getLeftRightStartAlign = (bounds: DOMRect, size: DOMRectReadOnly) => {
|
const getLeftRightStartAlign = (bounds: DOMRect, size: DOMRectReadOnly) => {
|
||||||
return {
|
return {
|
||||||
top: `${Math.min(bounds.y + 15, window.innerHeight - size.height - 20)}px`
|
top: `${Math.min(bounds.y, window.innerHeight - size.height - 20)}px`
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const getLeftRightMidAlign = (bounds: DOMRect, size: DOMRectReadOnly) => {
|
const getLeftRightMidAlign = (bounds: DOMRect, size: DOMRectReadOnly) => {
|
||||||
return {
|
return {
|
||||||
bottom: `${window.innerHeight - bounds.y - bounds.height - (size.height / 2)}px`
|
bottom: `${window.innerHeight - bounds.y - (bounds.height / 2) - (size.height / 2)}px`
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -49,19 +46,19 @@ const getLeftRightEndAlign = (bounds: DOMRect, size: DOMRectReadOnly) => {
|
|||||||
|
|
||||||
const getTopBotStartAlign = (bounds: DOMRect) => {
|
const getTopBotStartAlign = (bounds: DOMRect) => {
|
||||||
return {
|
return {
|
||||||
right: `${window.innerWidth - bounds.x + 10}px`
|
right: `${window.innerWidth - bounds.x - 6}px`
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const getTopBotMidAlign = (bounds: DOMRect, size: DOMRectReadOnly) => {
|
const getTopBotMidAlign = (bounds: DOMRect, size: DOMRectReadOnly) => {
|
||||||
return {
|
return {
|
||||||
right: `${window.innerWidth - bounds.x - (size.width / 2)}px`
|
right: `${window.innerWidth - bounds.x - (bounds.width / 2) - (size.width / 2)}px`
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const getTopBotEndAlign = (bounds: DOMRect) => {
|
const getTopBotEndAlign = (bounds: DOMRect) => {
|
||||||
return {
|
return {
|
||||||
left: `${bounds.x + bounds.width + 10}px`
|
left: `${bounds.x + bounds.width - 6}px`
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -4,9 +4,7 @@ import { makeStyles } from 'tss-react/mui';
|
|||||||
|
|
||||||
import { translate } from '../../../i18n/functions';
|
import { translate } from '../../../i18n/functions';
|
||||||
import Icon from '../../../icons/components/Icon';
|
import Icon from '../../../icons/components/Icon';
|
||||||
// eslint-disable-next-line lines-around-comment
|
import Tooltip from '../../../tooltip/components/Tooltip';
|
||||||
// @ts-ignore
|
|
||||||
import { Tooltip } from '../../../tooltip';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The type of the React {@code Component} props of {@link BaseIndicator}.
|
* The type of the React {@code Component} props of {@link BaseIndicator}.
|
||||||
@@ -58,7 +56,7 @@ interface IProps extends WithTranslation {
|
|||||||
* From which side of the indicator the tooltip should appear from,
|
* From which side of the indicator the tooltip should appear from,
|
||||||
* defaulting to "top".
|
* defaulting to "top".
|
||||||
*/
|
*/
|
||||||
tooltipPosition: string;
|
tooltipPosition: 'top' | 'bottom' | 'left' | 'right';
|
||||||
}
|
}
|
||||||
|
|
||||||
const useStyles = makeStyles()(() => {
|
const useStyles = makeStyles()(() => {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
import React, { Fragment } from 'react';
|
import React, { Fragment } from 'react';
|
||||||
|
|
||||||
import { Icon } from '../../icons';
|
import { Icon } from '../../icons';
|
||||||
import { Tooltip } from '../../tooltip';
|
import Tooltip from '../../tooltip/components/Tooltip';
|
||||||
import ContextMenuItem from '../../ui/components/web/ContextMenuItem';
|
import ContextMenuItem from '../../ui/components/web/ContextMenuItem';
|
||||||
|
|
||||||
import AbstractToolboxItem from './AbstractToolboxItem';
|
import AbstractToolboxItem from './AbstractToolboxItem';
|
||||||
|
|||||||
@@ -1,168 +0,0 @@
|
|||||||
// @flow
|
|
||||||
|
|
||||||
import React, { Component } from 'react';
|
|
||||||
|
|
||||||
import { Icon } from '../../../icons';
|
|
||||||
import { Tooltip } from '../../../tooltip';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The type of the React {@code Component} props of {@link OverflowMenuItem}.
|
|
||||||
*/
|
|
||||||
type Props = {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A succinct description of what the item does. Used by accessibility tools
|
|
||||||
* and torture tests.
|
|
||||||
*/
|
|
||||||
accessibilityLabel: string,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether menu item is disabled or not.
|
|
||||||
*/
|
|
||||||
disabled: boolean,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A React Element to display at the end of {@code OverflowMenuItem}.
|
|
||||||
*/
|
|
||||||
elementAfter?: React$Node,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The icon class to use for displaying an icon before the link text.
|
|
||||||
*/
|
|
||||||
icon: Object,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Id of the icon to be rendered.
|
|
||||||
*/
|
|
||||||
iconId?: string,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The callback to invoke when {@code OverflowMenuItem} is clicked.
|
|
||||||
*/
|
|
||||||
onClick: Function,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The text to display in the {@code OverflowMenuItem}.
|
|
||||||
*/
|
|
||||||
text: string,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The text to display in the tooltip.
|
|
||||||
*/
|
|
||||||
tooltip?: string,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* From which direction the tooltip should appear, relative to the button.
|
|
||||||
*/
|
|
||||||
tooltipPosition: string
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A React {@code Component} for displaying a link to interact with other
|
|
||||||
* features of the application.
|
|
||||||
*
|
|
||||||
* @augments Component
|
|
||||||
*/
|
|
||||||
class OverflowMenuItem extends Component<Props> {
|
|
||||||
/**
|
|
||||||
* Default values for {@code OverflowMenuItem} component's properties.
|
|
||||||
*
|
|
||||||
* @static
|
|
||||||
*/
|
|
||||||
static defaultProps = {
|
|
||||||
tooltipPosition: 'left',
|
|
||||||
disabled: false
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes a new {@code OverflowMenuItem} instance.
|
|
||||||
*
|
|
||||||
* @param {*} props - The read-only properties with which the new instance
|
|
||||||
* is to be initialized.
|
|
||||||
*/
|
|
||||||
constructor(props: Props) {
|
|
||||||
super(props);
|
|
||||||
|
|
||||||
// Bind event handler so it is only bound once for every instance.
|
|
||||||
this._onKeyPress = this._onKeyPress.bind(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
_onKeyPress: (Object) => void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* KeyPress handler for accessibility.
|
|
||||||
*
|
|
||||||
* @param {Object} e - The key event to handle.
|
|
||||||
*
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
_onKeyPress(e) {
|
|
||||||
if (!this.props.disabled && this.props.onClick && (e.key === ' ' || e.key === 'Enter')) {
|
|
||||||
e.preventDefault();
|
|
||||||
this.props.onClick();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Implements React's {@link Component#render()}.
|
|
||||||
*
|
|
||||||
* @inheritdoc
|
|
||||||
* @returns {ReactElement}
|
|
||||||
*/
|
|
||||||
render() {
|
|
||||||
const { accessibilityLabel, disabled, elementAfter, icon, iconId, onClick } = this.props;
|
|
||||||
|
|
||||||
let className = 'overflow-menu-item';
|
|
||||||
|
|
||||||
className += this.props.disabled ? ' disabled' : '';
|
|
||||||
|
|
||||||
return (
|
|
||||||
<li
|
|
||||||
aria-disabled = { disabled }
|
|
||||||
aria-label = { accessibilityLabel }
|
|
||||||
className = { className }
|
|
||||||
onClick = { disabled ? null : onClick }
|
|
||||||
onKeyPress = { this._onKeyPress }
|
|
||||||
role = 'button'
|
|
||||||
tabIndex = { 0 }>
|
|
||||||
<span className = 'overflow-menu-item-icon'>
|
|
||||||
<Icon
|
|
||||||
id = { iconId }
|
|
||||||
src = { icon } />
|
|
||||||
</span>
|
|
||||||
{ this._renderText() }
|
|
||||||
{
|
|
||||||
elementAfter || null
|
|
||||||
}
|
|
||||||
</li>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Renders the text label to display in the {@code OverflowMenuItem}.
|
|
||||||
*
|
|
||||||
* @private
|
|
||||||
* @returns {ReactElement}
|
|
||||||
*/
|
|
||||||
_renderText() {
|
|
||||||
const textElement = (
|
|
||||||
<span className = 'overflow-menu-item-text'>
|
|
||||||
{ this.props.text }
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
|
|
||||||
if (this.props.tooltip) {
|
|
||||||
return (
|
|
||||||
<Tooltip
|
|
||||||
content = { this.props.tooltip }
|
|
||||||
position = { this.props.tooltipPosition }>
|
|
||||||
{ textElement }
|
|
||||||
</Tooltip>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return textElement;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default OverflowMenuItem;
|
|
||||||
@@ -4,7 +4,7 @@ import React from 'react';
|
|||||||
|
|
||||||
import { NOTIFY_CLICK_MODE } from '../../../../toolbox/constants';
|
import { NOTIFY_CLICK_MODE } from '../../../../toolbox/constants';
|
||||||
import { Icon } from '../../../icons';
|
import { Icon } from '../../../icons';
|
||||||
import { Tooltip } from '../../../tooltip';
|
import Tooltip from '../../../tooltip/components/Tooltip';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
|
|
||||||
@@ -107,12 +107,13 @@ export default function ToolboxButtonWithIcon(props: Props) {
|
|||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const iconProps = {};
|
const iconProps = {};
|
||||||
|
let className = '';
|
||||||
|
|
||||||
if (iconDisabled) {
|
if (iconDisabled) {
|
||||||
iconProps.className
|
className
|
||||||
= 'settings-button-small-icon settings-button-small-icon--disabled';
|
= 'settings-button-small-icon settings-button-small-icon--disabled';
|
||||||
} else {
|
} else {
|
||||||
iconProps.className = 'settings-button-small-icon';
|
className = 'settings-button-small-icon';
|
||||||
iconProps.onClick = () => {
|
iconProps.onClick = () => {
|
||||||
if (typeof APP !== 'undefined' && notifyMode) {
|
if (typeof APP !== 'undefined' && notifyMode) {
|
||||||
APP.API.notifyToolbarButtonClicked(
|
APP.API.notifyToolbarButtonClicked(
|
||||||
@@ -141,6 +142,7 @@ export default function ToolboxButtonWithIcon(props: Props) {
|
|||||||
|
|
||||||
<div>
|
<div>
|
||||||
<Tooltip
|
<Tooltip
|
||||||
|
containerClassName = { className }
|
||||||
content = { iconTooltip }
|
content = { iconTooltip }
|
||||||
position = 'top'>
|
position = 'top'>
|
||||||
<Icon
|
<Icon
|
||||||
|
|||||||
@@ -1,111 +0,0 @@
|
|||||||
// @flow
|
|
||||||
|
|
||||||
import React, { Fragment } from 'react';
|
|
||||||
|
|
||||||
import { Icon } from '../../../icons';
|
|
||||||
import { Tooltip } from '../../../tooltip';
|
|
||||||
import AbstractToolboxItem from '../AbstractToolboxItem';
|
|
||||||
import type { Props } from '../AbstractToolboxItem';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Web implementation of {@code AbstractToolboxItem}.
|
|
||||||
*/
|
|
||||||
export default class ToolboxItem extends AbstractToolboxItem<Props> {
|
|
||||||
/**
|
|
||||||
* Initializes a new {@code ToolboxItem} instance.
|
|
||||||
*
|
|
||||||
* @inheritdoc
|
|
||||||
*/
|
|
||||||
constructor(props: Props) {
|
|
||||||
super(props);
|
|
||||||
|
|
||||||
this._onKeyPress = this._onKeyPress.bind(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
_onKeyPress: (Object) => void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles 'Enter' and Space key on the button to trigger onClick for accessibility.
|
|
||||||
*
|
|
||||||
* @param {Object} event - The key event.
|
|
||||||
* @private
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
_onKeyPress(event) {
|
|
||||||
if (event.key === 'Enter' || event.key === ' ') {
|
|
||||||
event.preventDefault();
|
|
||||||
this.props.onClick();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles rendering of the actual item. If the label is being shown, which
|
|
||||||
* is controlled with the `showLabel` prop, the item is rendered for its
|
|
||||||
* display in an overflow menu, otherwise it will only have an icon, which
|
|
||||||
* can be displayed on any toolbar.
|
|
||||||
*
|
|
||||||
* @protected
|
|
||||||
* @returns {ReactElement}
|
|
||||||
*/
|
|
||||||
_renderItem() {
|
|
||||||
const {
|
|
||||||
disabled,
|
|
||||||
elementAfter,
|
|
||||||
onClick,
|
|
||||||
showLabel,
|
|
||||||
tooltipPosition,
|
|
||||||
toggled
|
|
||||||
} = this.props;
|
|
||||||
const className = showLabel ? 'overflow-menu-item' : 'toolbox-button';
|
|
||||||
const props = {
|
|
||||||
'aria-pressed': toggled,
|
|
||||||
'aria-disabled': disabled,
|
|
||||||
'aria-label': this.accessibilityLabel,
|
|
||||||
className: className + (disabled ? ' disabled' : ''),
|
|
||||||
onClick: disabled ? undefined : onClick,
|
|
||||||
onKeyPress: this._onKeyPress,
|
|
||||||
tabIndex: 0,
|
|
||||||
role: 'button'
|
|
||||||
};
|
|
||||||
|
|
||||||
const elementType = showLabel ? 'li' : 'div';
|
|
||||||
const useTooltip = this.tooltip && this.tooltip.length > 0;
|
|
||||||
let children = (
|
|
||||||
<Fragment>
|
|
||||||
{ this._renderIcon() }
|
|
||||||
{ showLabel && <span>
|
|
||||||
{ this.label }
|
|
||||||
</span> }
|
|
||||||
{ elementAfter }
|
|
||||||
</Fragment>
|
|
||||||
);
|
|
||||||
|
|
||||||
if (useTooltip) {
|
|
||||||
children = (
|
|
||||||
<Tooltip
|
|
||||||
content = { this.tooltip }
|
|
||||||
position = { tooltipPosition }>
|
|
||||||
{ children }
|
|
||||||
</Tooltip>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return React.createElement(elementType, props, children);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper function to render the item's icon.
|
|
||||||
*
|
|
||||||
* @private
|
|
||||||
* @returns {ReactElement}
|
|
||||||
*/
|
|
||||||
_renderIcon() {
|
|
||||||
const { customClass, disabled, icon, showLabel, toggled } = this.props;
|
|
||||||
const iconComponent = <Icon src = { icon } />;
|
|
||||||
const elementType = showLabel ? 'span' : 'div';
|
|
||||||
const className = `${showLabel ? 'overflow-menu-item-icon' : 'toolbox-icon'} ${
|
|
||||||
toggled ? 'toggled' : ''} ${disabled ? 'disabled' : ''} ${customClass ?? ''}`;
|
|
||||||
|
|
||||||
return React.createElement(elementType, { className }, iconComponent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
// @flow
|
// @flow
|
||||||
|
|
||||||
export { default as OverflowMenuItem } from './OverflowMenuItem';
|
|
||||||
export { default as ToolboxButtonWithIcon } from './ToolboxButtonWithIcon';
|
export { default as ToolboxButtonWithIcon } from './ToolboxButtonWithIcon';
|
||||||
|
|||||||
19
react/features/base/tooltip/actionTypes.tsx
Normal file
19
react/features/base/tooltip/actionTypes.tsx
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
/**
|
||||||
|
* The type of the action which signals a tooltip is being displayed.
|
||||||
|
*
|
||||||
|
* {
|
||||||
|
* type: SHOW_TOOLTIP,
|
||||||
|
* content: string
|
||||||
|
* }.
|
||||||
|
*/
|
||||||
|
export const SHOW_TOOLTIP = 'SHOW_TOOLTIP';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The type of the action which signals a tooltip should be hidden.
|
||||||
|
*
|
||||||
|
* {
|
||||||
|
* type: SHOW_TOOLTIP,
|
||||||
|
* content: string
|
||||||
|
* }.
|
||||||
|
*/
|
||||||
|
export const HIDE_TOOLTIP = 'HIDE_TOOLTIP';
|
||||||
29
react/features/base/tooltip/actions.tsx
Normal file
29
react/features/base/tooltip/actions.tsx
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import { HIDE_TOOLTIP, SHOW_TOOLTIP } from './actionTypes';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set tooltip state to visible.
|
||||||
|
*
|
||||||
|
* @param {string} content - The content of the tooltip.
|
||||||
|
* Used as unique identifier for tooltip.
|
||||||
|
* @returns {Object}
|
||||||
|
*/
|
||||||
|
export function showTooltip(content: string) {
|
||||||
|
return {
|
||||||
|
type: SHOW_TOOLTIP,
|
||||||
|
content
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set tooltip state to hidden.
|
||||||
|
*
|
||||||
|
* @param {string} content - The content of the tooltip.
|
||||||
|
* Used as unique identifier for tooltip.
|
||||||
|
* @returns {Object}
|
||||||
|
*/
|
||||||
|
export function hideTooltip(content: string) {
|
||||||
|
return {
|
||||||
|
type: HIDE_TOOLTIP,
|
||||||
|
content
|
||||||
|
};
|
||||||
|
}
|
||||||
150
react/features/base/tooltip/components/Tooltip.tsx
Normal file
150
react/features/base/tooltip/components/Tooltip.tsx
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
import React, { ReactElement, useCallback, useEffect, useRef, useState } from 'react';
|
||||||
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
|
import { keyframes } from 'tss-react';
|
||||||
|
import { makeStyles } from 'tss-react/mui';
|
||||||
|
|
||||||
|
import { IReduxState } from '../../../app/types';
|
||||||
|
import { isMobileBrowser } from '../../environment/utils';
|
||||||
|
import Popover from '../../popover/components/Popover.web';
|
||||||
|
import { withPixelLineHeight } from '../../styles/functions.web';
|
||||||
|
import { hideTooltip, showTooltip } from '../actions';
|
||||||
|
|
||||||
|
const TOOLTIP_DELAY = 300;
|
||||||
|
const ANIMATION_DURATION = 0.2;
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
children: ReactElement;
|
||||||
|
containerClassName?: string;
|
||||||
|
content: string;
|
||||||
|
position?: 'top' | 'bottom' | 'left' | 'right';
|
||||||
|
}
|
||||||
|
|
||||||
|
const useStyles = makeStyles()(theme => {
|
||||||
|
return {
|
||||||
|
container: {
|
||||||
|
backgroundColor: theme.palette.uiBackground,
|
||||||
|
borderRadius: '3px',
|
||||||
|
padding: theme.spacing(2),
|
||||||
|
...withPixelLineHeight(theme.typography.labelRegular),
|
||||||
|
color: theme.palette.text01,
|
||||||
|
position: 'relative',
|
||||||
|
|
||||||
|
'&.mounting-animation': {
|
||||||
|
animation: `${keyframes`
|
||||||
|
0% {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
`} ${ANIMATION_DURATION}s forwards ease-in`
|
||||||
|
},
|
||||||
|
|
||||||
|
'&.unmounting': {
|
||||||
|
animation: `${keyframes`
|
||||||
|
0% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
`} ${ANIMATION_DURATION}s forwards ease-out`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const Tooltip = ({ containerClassName, content, children, position = 'top' }: IProps) => {
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const [ visible, setVisible ] = useState(false);
|
||||||
|
const [ isUnmounting, setIsUnmounting ] = useState(false);
|
||||||
|
const { classes, cx } = useStyles();
|
||||||
|
const timeoutID = useRef({
|
||||||
|
open: 0,
|
||||||
|
close: 0
|
||||||
|
});
|
||||||
|
const {
|
||||||
|
content: storeContent,
|
||||||
|
previousContent,
|
||||||
|
visible: isVisible
|
||||||
|
} = useSelector((state: IReduxState) => state['features/base/tooltip']);
|
||||||
|
|
||||||
|
if (isMobileBrowser()) {
|
||||||
|
return children;
|
||||||
|
}
|
||||||
|
|
||||||
|
const contentComponent = (
|
||||||
|
<div
|
||||||
|
className = { cx(classes.container, previousContent === '' && 'mounting-animation',
|
||||||
|
isUnmounting && 'unmounting') }>
|
||||||
|
{content}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
const openPopover = () => {
|
||||||
|
setVisible(true);
|
||||||
|
dispatch(showTooltip(content));
|
||||||
|
};
|
||||||
|
|
||||||
|
const closePopover = () => {
|
||||||
|
setVisible(false);
|
||||||
|
dispatch(hideTooltip(content));
|
||||||
|
setIsUnmounting(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onPopoverOpen = useCallback(() => {
|
||||||
|
clearTimeout(timeoutID.current.close);
|
||||||
|
timeoutID.current.close = 0;
|
||||||
|
if (!visible) {
|
||||||
|
if (isVisible) {
|
||||||
|
openPopover();
|
||||||
|
} else {
|
||||||
|
timeoutID.current.open = window.setTimeout(() => {
|
||||||
|
openPopover();
|
||||||
|
}, TOOLTIP_DELAY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [ visible, isVisible ]);
|
||||||
|
|
||||||
|
const onPopoverClose = useCallback(() => {
|
||||||
|
clearTimeout(timeoutID.current.open);
|
||||||
|
if (visible) {
|
||||||
|
timeoutID.current.close = window.setTimeout(() => {
|
||||||
|
setIsUnmounting(true);
|
||||||
|
}, TOOLTIP_DELAY);
|
||||||
|
}
|
||||||
|
}, [ visible ]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (isUnmounting) {
|
||||||
|
setTimeout(() => {
|
||||||
|
if (timeoutID.current.close !== 0) {
|
||||||
|
closePopover();
|
||||||
|
}
|
||||||
|
}, (ANIMATION_DURATION * 1000) + 10);
|
||||||
|
}
|
||||||
|
}, [ isUnmounting ]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (storeContent !== content) {
|
||||||
|
closePopover();
|
||||||
|
clearTimeout(timeoutID.current.close);
|
||||||
|
timeoutID.current.close = 0;
|
||||||
|
}
|
||||||
|
}, [ storeContent ]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Popover
|
||||||
|
allowClick = { true }
|
||||||
|
className = { containerClassName }
|
||||||
|
content = { contentComponent }
|
||||||
|
onPopoverClose = { onPopoverClose }
|
||||||
|
onPopoverOpen = { onPopoverOpen }
|
||||||
|
position = { position }
|
||||||
|
visible = { visible }>
|
||||||
|
{children}
|
||||||
|
</Popover>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Tooltip;
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
// @flow
|
|
||||||
|
|
||||||
import Tooltip from '@atlaskit/tooltip';
|
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
import { isMobileBrowser } from '../../environment/utils';
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Children of the component.
|
|
||||||
*/
|
|
||||||
children: React$Node,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The text to be displayed in the tooltip.
|
|
||||||
*/
|
|
||||||
content?: string | null,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The position of the tooltip relative to the element it contains.
|
|
||||||
*/
|
|
||||||
position?: string
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Wrapper of AtlasKit Tooltip that doesn't render the actual tooltip in mobile browsers.
|
|
||||||
*
|
|
||||||
* @returns {ReactElement}
|
|
||||||
*/
|
|
||||||
function TooltipWrapper({
|
|
||||||
children,
|
|
||||||
content,
|
|
||||||
position
|
|
||||||
}: Props) {
|
|
||||||
if (isMobileBrowser()) {
|
|
||||||
return children;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Tooltip
|
|
||||||
content = { content }
|
|
||||||
position = { position }>
|
|
||||||
{children}
|
|
||||||
</Tooltip>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default TooltipWrapper;
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
// @flow
|
|
||||||
|
|
||||||
export { default as Tooltip } from './TooltipWrapper';
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
// @flow
|
|
||||||
|
|
||||||
export * from './components';
|
|
||||||
50
react/features/base/tooltip/reducer.ts
Normal file
50
react/features/base/tooltip/reducer.ts
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
import ReducerRegistry from '../redux/ReducerRegistry';
|
||||||
|
|
||||||
|
import { HIDE_TOOLTIP, SHOW_TOOLTIP } from './actionTypes';
|
||||||
|
|
||||||
|
export interface ITooltipState {
|
||||||
|
content: string;
|
||||||
|
previousContent: string;
|
||||||
|
visible: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const DEFAULT_STATE = {
|
||||||
|
content: '',
|
||||||
|
previousContent: '',
|
||||||
|
visible: false
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reduces redux actions which mark the tooltip as displayed or hidden.
|
||||||
|
*
|
||||||
|
* @param {IDialogState} state - The current redux state.
|
||||||
|
* @param {Action} action - The redux action to reduce.
|
||||||
|
* @param {string} action.type - The type of the redux action to reduce..
|
||||||
|
* @returns {State} The next redux state that is the result of reducing the
|
||||||
|
* specified action.
|
||||||
|
*/
|
||||||
|
ReducerRegistry.register<ITooltipState>('features/base/tooltip', (state = DEFAULT_STATE, action): ITooltipState => {
|
||||||
|
switch (action.type) {
|
||||||
|
case SHOW_TOOLTIP:
|
||||||
|
return {
|
||||||
|
content: action.content,
|
||||||
|
previousContent: state.content,
|
||||||
|
visible: true
|
||||||
|
};
|
||||||
|
case HIDE_TOOLTIP: {
|
||||||
|
// The tooltip can be marked as hidden only if the hide action
|
||||||
|
// is dispatched by the tooltip that is displayed.
|
||||||
|
if (action.content === state.content) {
|
||||||
|
return {
|
||||||
|
content: '',
|
||||||
|
previousContent: '',
|
||||||
|
visible: false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return state;
|
||||||
|
});
|
||||||
@@ -10,7 +10,7 @@ import {
|
|||||||
import { translate } from '../../base/i18n';
|
import { translate } from '../../base/i18n';
|
||||||
import { Icon, IconPlus } from '../../base/icons';
|
import { Icon, IconPlus } from '../../base/icons';
|
||||||
import { connect } from '../../base/redux';
|
import { connect } from '../../base/redux';
|
||||||
import { Tooltip } from '../../base/tooltip';
|
import Tooltip from '../../base/tooltip/components/Tooltip';
|
||||||
import { updateCalendarEvent } from '../actions';
|
import { updateCalendarEvent } from '../actions';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import React, { Component } from 'react';
|
|||||||
|
|
||||||
import { translate } from '../../base/i18n';
|
import { translate } from '../../base/i18n';
|
||||||
import { Icon, IconPlus } from '../../base/icons';
|
import { Icon, IconPlus } from '../../base/icons';
|
||||||
import { Tooltip } from '../../base/tooltip';
|
import Tooltip from '../../base/tooltip/components/Tooltip';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The type of the React {@code Component} props of {@link JoinButton}.
|
* The type of the React {@code Component} props of {@link JoinButton}.
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import Tooltip from '@atlaskit/tooltip';
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
@@ -6,6 +5,7 @@ import { translate } from '../../../base/i18n/functions';
|
|||||||
import { IconExclamationTriangle } from '../../../base/icons/svg';
|
import { IconExclamationTriangle } from '../../../base/icons/svg';
|
||||||
import Label from '../../../base/label/components/web/Label';
|
import Label from '../../../base/label/components/web/Label';
|
||||||
import { COLORS } from '../../../base/label/constants';
|
import { COLORS } from '../../../base/label/constants';
|
||||||
|
import Tooltip from '../../../base/tooltip/components/Tooltip';
|
||||||
import AbstractInsecureRoomNameLabel, { _mapStateToProps } from '../AbstractInsecureRoomNameLabel';
|
import AbstractInsecureRoomNameLabel, { _mapStateToProps } from '../AbstractInsecureRoomNameLabel';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -6,9 +6,7 @@ import { makeStyles } from 'tss-react/mui';
|
|||||||
import { IReduxState } from '../../../app/types';
|
import { IReduxState } from '../../../app/types';
|
||||||
import { IconRaiseHand } from '../../../base/icons/svg';
|
import { IconRaiseHand } from '../../../base/icons/svg';
|
||||||
import Label from '../../../base/label/components/web/Label';
|
import Label from '../../../base/label/components/web/Label';
|
||||||
// eslint-disable-next-line lines-around-comment
|
import Tooltip from '../../../base/tooltip/components/Tooltip';
|
||||||
// @ts-ignore
|
|
||||||
import { Tooltip } from '../../../base/tooltip';
|
|
||||||
import { open as openParticipantsPane } from '../../../participants-pane/actions.web';
|
import { open as openParticipantsPane } from '../../../participants-pane/actions.web';
|
||||||
|
|
||||||
const useStyles = makeStyles()(theme => {
|
const useStyles = makeStyles()(theme => {
|
||||||
|
|||||||
@@ -5,9 +5,7 @@ import { makeStyles } from 'tss-react/mui';
|
|||||||
|
|
||||||
import { getConferenceName } from '../../../base/conference/functions';
|
import { getConferenceName } from '../../../base/conference/functions';
|
||||||
import { withPixelLineHeight } from '../../../base/styles/functions.web';
|
import { withPixelLineHeight } from '../../../base/styles/functions.web';
|
||||||
// eslint-disable-next-line lines-around-comment
|
import Tooltip from '../../../base/tooltip/components/Tooltip';
|
||||||
// @ts-ignore
|
|
||||||
import { Tooltip } from '../../../base/tooltip';
|
|
||||||
|
|
||||||
const useStyles = makeStyles()(theme => {
|
const useStyles = makeStyles()(theme => {
|
||||||
return {
|
return {
|
||||||
@@ -44,13 +42,13 @@ const SubjectText = () => {
|
|||||||
const { classes } = useStyles();
|
const { classes } = useStyles();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className = { classes.container }>
|
<Tooltip
|
||||||
<Tooltip
|
content = { subject }
|
||||||
content = { subject }
|
position = 'bottom'>
|
||||||
position = 'bottom'>
|
<div className = { classes.container }>
|
||||||
<div className = { clsx('subject-text--content', classes.content) }>{subject}</div>
|
<div className = { clsx('subject-text--content', classes.content) }>{subject}</div>
|
||||||
</Tooltip>
|
</div>
|
||||||
</div>
|
</Tooltip>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -5,9 +5,7 @@ import { useDispatch, useSelector } from 'react-redux';
|
|||||||
import { IReduxState } from '../../../app/types';
|
import { IReduxState } from '../../../app/types';
|
||||||
import { IconArrowDown } from '../../../base/icons/svg/index';
|
import { IconArrowDown } from '../../../base/icons/svg/index';
|
||||||
import Label from '../../../base/label/components/web/Label';
|
import Label from '../../../base/label/components/web/Label';
|
||||||
// eslint-disable-next-line lines-around-comment
|
import Tooltip from '../../../base/tooltip/components/Tooltip';
|
||||||
// @ts-ignore
|
|
||||||
import { Tooltip } from '../../../base/tooltip';
|
|
||||||
import { setTopPanelVisible } from '../../../filmstrip/actions.web';
|
import { setTopPanelVisible } from '../../../filmstrip/actions.web';
|
||||||
|
|
||||||
const ToggleTopPanelLabel = () => {
|
const ToggleTopPanelLabel = () => {
|
||||||
|
|||||||
@@ -230,12 +230,9 @@ const styles = (theme: Theme) => {
|
|||||||
},
|
},
|
||||||
contextMenu: {
|
contextMenu: {
|
||||||
position: 'relative' as const,
|
position: 'relative' as const,
|
||||||
marginTop: 0,
|
margin: 0,
|
||||||
right: 'auto',
|
right: 'auto',
|
||||||
padding: `${theme.spacing(2)} ${theme.spacing(1)}`,
|
padding: `${theme.spacing(2)} ${theme.spacing(1)}`
|
||||||
marginLeft: theme.spacing(1),
|
|
||||||
marginRight: theme.spacing(1),
|
|
||||||
marginBottom: theme.spacing(1)
|
|
||||||
},
|
},
|
||||||
download: {},
|
download: {},
|
||||||
mobile: {
|
mobile: {
|
||||||
|
|||||||
@@ -10,9 +10,7 @@ import {
|
|||||||
} from '../../../base/participants/functions';
|
} from '../../../base/participants/functions';
|
||||||
import { updateSettings } from '../../../base/settings/actions';
|
import { updateSettings } from '../../../base/settings/actions';
|
||||||
import { withPixelLineHeight } from '../../../base/styles/functions.web';
|
import { withPixelLineHeight } from '../../../base/styles/functions.web';
|
||||||
// eslint-disable-next-line lines-around-comment
|
import Tooltip from '../../../base/tooltip/components/Tooltip';
|
||||||
// @ts-ignore
|
|
||||||
import { Tooltip } from '../../../base/tooltip';
|
|
||||||
import { getIndicatorsTooltipPosition } from '../../../filmstrip/functions.web';
|
import { getIndicatorsTooltipPosition } from '../../../filmstrip/functions.web';
|
||||||
import { appendSuffix } from '../../functions';
|
import { appendSuffix } from '../../functions';
|
||||||
|
|
||||||
|
|||||||
@@ -5,9 +5,7 @@ import { translate } from '../../base/i18n/functions';
|
|||||||
import { IconE2EE } from '../../base/icons/svg';
|
import { IconE2EE } from '../../base/icons/svg';
|
||||||
import Label from '../../base/label/components/web/Label';
|
import Label from '../../base/label/components/web/Label';
|
||||||
import { COLORS } from '../../base/label/constants';
|
import { COLORS } from '../../base/label/constants';
|
||||||
// eslint-disable-next-line lines-around-comment
|
import Tooltip from '../../base/tooltip/components/Tooltip';
|
||||||
// @ts-ignore
|
|
||||||
import { Tooltip } from '../../base/tooltip';
|
|
||||||
|
|
||||||
import { IProps, _mapStateToProps } from './AbstractE2EELabel';
|
import { IProps, _mapStateToProps } from './AbstractE2EELabel';
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ interface IProps {
|
|||||||
/**
|
/**
|
||||||
* From which side of the indicator the tooltip should appear from.
|
* From which side of the indicator the tooltip should appear from.
|
||||||
*/
|
*/
|
||||||
tooltipPosition: string;
|
tooltipPosition: 'top' | 'bottom' | 'left' | 'right';
|
||||||
}
|
}
|
||||||
|
|
||||||
const useStyles = makeStyles()(() => {
|
const useStyles = makeStyles()(() => {
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ interface IProps {
|
|||||||
/**
|
/**
|
||||||
* From which side of the indicator the tooltip should appear from.
|
* From which side of the indicator the tooltip should appear from.
|
||||||
*/
|
*/
|
||||||
tooltipPosition: string;
|
tooltipPosition: 'top' | 'bottom' | 'left' | 'right';
|
||||||
}
|
}
|
||||||
|
|
||||||
const useStyles = makeStyles()(theme => {
|
const useStyles = makeStyles()(theme => {
|
||||||
|
|||||||
@@ -30,8 +30,7 @@ import {
|
|||||||
} from '../../../base/participants/functions';
|
} from '../../../base/participants/functions';
|
||||||
import { IParticipant } from '../../../base/participants/types';
|
import { IParticipant } from '../../../base/participants/types';
|
||||||
import { ASPECT_RATIO_NARROW } from '../../../base/responsive-ui/constants';
|
import { ASPECT_RATIO_NARROW } from '../../../base/responsive-ui/constants';
|
||||||
// @ts-ignore
|
import Tooltip from '../../../base/tooltip/components/Tooltip';
|
||||||
import { Tooltip } from '../../../base/tooltip';
|
|
||||||
import { trackStreamingStatusChanged } from '../../../base/tracks/actions';
|
import { trackStreamingStatusChanged } from '../../../base/tracks/actions';
|
||||||
import {
|
import {
|
||||||
getLocalAudioTrack,
|
getLocalAudioTrack,
|
||||||
|
|||||||
@@ -222,7 +222,9 @@ export const STATS_POPOVER_POSITION = {
|
|||||||
/**
|
/**
|
||||||
* The tooltip position for the indicators on the thumbnail.
|
* The tooltip position for the indicators on the thumbnail.
|
||||||
*/
|
*/
|
||||||
export const INDICATORS_TOOLTIP_POSITION = {
|
export const INDICATORS_TOOLTIP_POSITION: {
|
||||||
|
[x: string]: 'right' | 'left' | 'top';
|
||||||
|
} = {
|
||||||
[THUMBNAIL_TYPE.TILE]: 'right',
|
[THUMBNAIL_TYPE.TILE]: 'right',
|
||||||
[THUMBNAIL_TYPE.VERTICAL]: 'left',
|
[THUMBNAIL_TYPE.VERTICAL]: 'left',
|
||||||
[THUMBNAIL_TYPE.HORIZONTAL]: 'top'
|
[THUMBNAIL_TYPE.HORIZONTAL]: 'top'
|
||||||
|
|||||||
@@ -11,9 +11,7 @@ import {
|
|||||||
IconOffice365,
|
IconOffice365,
|
||||||
IconYahoo
|
IconYahoo
|
||||||
} from '../../../../base/icons/svg';
|
} from '../../../../base/icons/svg';
|
||||||
// eslint-disable-next-line lines-around-comment
|
import Tooltip from '../../../../base/tooltip/components/Tooltip';
|
||||||
// @ts-ignore
|
|
||||||
import { Tooltip } from '../../../../base/tooltip';
|
|
||||||
import { copyText } from '../../../../base/util/copyText.web';
|
import { copyText } from '../../../../base/util/copyText.web';
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { Tooltip } from '../../../base/tooltip';
|
import Tooltip from '../../../base/tooltip/components/Tooltip';
|
||||||
import AbstractToolbarButton from '../../../toolbox/components/AbstractToolbarButton';
|
import AbstractToolbarButton from '../../../toolbox/components/AbstractToolbarButton';
|
||||||
import type { Props as AbstractToolbarButtonProps } from '../../../toolbox/components/AbstractToolbarButton';
|
import type { Props as AbstractToolbarButtonProps } from '../../../toolbox/components/AbstractToolbarButton';
|
||||||
|
|
||||||
|
|||||||
@@ -10,9 +10,7 @@ import { IconHighlight } from '../../../../base/icons/svg';
|
|||||||
import { MEET_FEATURES } from '../../../../base/jwt/constants';
|
import { MEET_FEATURES } from '../../../../base/jwt/constants';
|
||||||
import Label from '../../../../base/label/components/web/Label';
|
import Label from '../../../../base/label/components/web/Label';
|
||||||
import { connect } from '../../../../base/redux/functions';
|
import { connect } from '../../../../base/redux/functions';
|
||||||
// eslint-disable-next-line lines-around-comment
|
import Tooltip from '../../../../base/tooltip/components/Tooltip';
|
||||||
// @ts-ignore
|
|
||||||
import { Tooltip } from '../../../../base/tooltip';
|
|
||||||
import BaseTheme from '../../../../base/ui/components/BaseTheme.web';
|
import BaseTheme from '../../../../base/ui/components/BaseTheme.web';
|
||||||
import { maybeShowPremiumFeatureDialog } from '../../../../jaas/actions';
|
import { maybeShowPremiumFeatureDialog } from '../../../../jaas/actions';
|
||||||
import AbstractHighlightButton, {
|
import AbstractHighlightButton, {
|
||||||
|
|||||||
@@ -7,9 +7,7 @@ import { IconRecord, IconSites } from '../../../base/icons/svg';
|
|||||||
import Label from '../../../base/label/components/web/Label';
|
import Label from '../../../base/label/components/web/Label';
|
||||||
import { JitsiRecordingConstants } from '../../../base/lib-jitsi-meet';
|
import { JitsiRecordingConstants } from '../../../base/lib-jitsi-meet';
|
||||||
import { connect } from '../../../base/redux/functions';
|
import { connect } from '../../../base/redux/functions';
|
||||||
// eslint-disable-next-line lines-around-comment
|
import Tooltip from '../../../base/tooltip/components/Tooltip';
|
||||||
// @ts-ignore
|
|
||||||
import { Tooltip } from '../../../base/tooltip';
|
|
||||||
import AbstractRecordingLabel, {
|
import AbstractRecordingLabel, {
|
||||||
_mapStateToProps
|
_mapStateToProps
|
||||||
|
|
||||||
|
|||||||
@@ -14,9 +14,7 @@ import {
|
|||||||
IconEmotionsSad,
|
IconEmotionsSad,
|
||||||
IconEmotionsSurprised
|
IconEmotionsSurprised
|
||||||
} from '../../../base/icons/svg';
|
} from '../../../base/icons/svg';
|
||||||
// eslint-disable-next-line lines-around-comment
|
import Tooltip from '../../../base/tooltip/components/Tooltip';
|
||||||
// @ts-ignore
|
|
||||||
import { Tooltip } from '../../../base/tooltip';
|
|
||||||
import Dialog from '../../../base/ui/components/web/Dialog';
|
import Dialog from '../../../base/ui/components/web/Dialog';
|
||||||
import { escapeRegexp } from '../../../base/util/helpers';
|
import { escapeRegexp } from '../../../base/util/helpers';
|
||||||
import { initSearch, resetSearchCriteria, toggleFaceExpressions } from '../../actions.any';
|
import { initSearch, resetSearchCriteria, toggleFaceExpressions } from '../../actions.any';
|
||||||
|
|||||||
@@ -31,6 +31,12 @@ type Props = {
|
|||||||
* Custom style to apply to the container div.
|
* Custom style to apply to the container div.
|
||||||
*/
|
*/
|
||||||
style?: any;
|
style?: any;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The selector for the element we consider the content container.
|
||||||
|
* This is used to determine the correct size of the portal content.
|
||||||
|
*/
|
||||||
|
targetSelector?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -39,7 +45,7 @@ type Props = {
|
|||||||
*
|
*
|
||||||
* @returns {ReactElement}
|
* @returns {ReactElement}
|
||||||
*/
|
*/
|
||||||
function DialogPortal({ children, className, style, getRef, setSize }: Props) {
|
function DialogPortal({ children, className, style, getRef, setSize, targetSelector }: Props) {
|
||||||
const clientWidth = useSelector((state: IReduxState) => state['features/base/responsive-ui'].clientWidth);
|
const clientWidth = useSelector((state: IReduxState) => state['features/base/responsive-ui'].clientWidth);
|
||||||
const [ portalTarget ] = useState(() => {
|
const [ portalTarget ] = useState(() => {
|
||||||
const portalDiv = document.createElement('div');
|
const portalDiv = document.createElement('div');
|
||||||
@@ -87,13 +93,15 @@ function DialogPortal({ children, className, style, getRef, setSize }: Props) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const target = targetSelector ? portalTarget.querySelector(targetSelector) : portalTarget;
|
||||||
|
|
||||||
if (document.body) {
|
if (document.body) {
|
||||||
document.body.appendChild(portalTarget);
|
document.body.appendChild(portalTarget);
|
||||||
observer.observe(portalTarget);
|
observer.observe(target ?? portalTarget);
|
||||||
}
|
}
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
observer.unobserve(portalTarget);
|
observer.unobserve(target ?? portalTarget);
|
||||||
if (document.body) {
|
if (document.body) {
|
||||||
document.body.removeChild(portalTarget);
|
document.body.removeChild(portalTarget);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,9 +3,7 @@ import { connect } from 'react-redux';
|
|||||||
|
|
||||||
import { translate } from '../../base/i18n/functions';
|
import { translate } from '../../base/i18n/functions';
|
||||||
import Label from '../../base/label/components/web/Label';
|
import Label from '../../base/label/components/web/Label';
|
||||||
// eslint-disable-next-line lines-around-comment
|
import Tooltip from '../../base/tooltip/components/Tooltip';
|
||||||
// @ts-ignore
|
|
||||||
import { Tooltip } from '../../base/tooltip';
|
|
||||||
|
|
||||||
import { Props, _mapStateToProps } from './AbstractTranscribingLabel';
|
import { Props, _mapStateToProps } from './AbstractTranscribingLabel';
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import { IconPerformance } from '../../base/icons';
|
|||||||
import { Label } from '../../base/label';
|
import { Label } from '../../base/label';
|
||||||
import { COLORS } from '../../base/label/constants';
|
import { COLORS } from '../../base/label/constants';
|
||||||
import { connect } from '../../base/redux';
|
import { connect } from '../../base/redux';
|
||||||
import { Tooltip } from '../../base/tooltip';
|
import Tooltip from '../../base/tooltip/components/Tooltip';
|
||||||
import { shouldDisplayTileView } from '../../video-layout';
|
import { shouldDisplayTileView } from '../../video-layout';
|
||||||
|
|
||||||
import AbstractVideoQualityLabel, {
|
import AbstractVideoQualityLabel, {
|
||||||
|
|||||||
@@ -14,8 +14,7 @@ import { translate } from '../../base/i18n/functions';
|
|||||||
import Icon from '../../base/icons/components/Icon';
|
import Icon from '../../base/icons/components/Icon';
|
||||||
import { IconCloseLarge } from '../../base/icons/svg';
|
import { IconCloseLarge } from '../../base/icons/svg';
|
||||||
import { withPixelLineHeight } from '../../base/styles/functions.web';
|
import { withPixelLineHeight } from '../../base/styles/functions.web';
|
||||||
// @ts-ignore
|
import Tooltip from '../../base/tooltip/components/Tooltip';
|
||||||
import { Tooltip } from '../../base/tooltip';
|
|
||||||
import Spinner from '../../base/ui/components/web/Spinner';
|
import Spinner from '../../base/ui/components/web/Spinner';
|
||||||
import { BACKGROUNDS_LIMIT, IMAGES, type Image, VIRTUAL_BACKGROUND_TYPE } from '../constants';
|
import { BACKGROUNDS_LIMIT, IMAGES, type Image, VIRTUAL_BACKGROUND_TYPE } from '../constants';
|
||||||
import { toDataURL } from '../functions';
|
import { toDataURL } from '../functions';
|
||||||
@@ -430,7 +429,7 @@ function VirtualBackgrounds({
|
|||||||
</Tooltip>
|
</Tooltip>
|
||||||
{_images.map(image => (
|
{_images.map(image => (
|
||||||
<Tooltip
|
<Tooltip
|
||||||
content = { image.tooltip && t(`virtualBackground.${image.tooltip}`) }
|
content = { (image.tooltip && t(`virtualBackground.${image.tooltip}`)) ?? '' }
|
||||||
key = { image.id }
|
key = { image.id }
|
||||||
position = { 'top' }>
|
position = { 'top' }>
|
||||||
<img
|
<img
|
||||||
|
|||||||
@@ -6,9 +6,7 @@ import { makeStyles } from 'tss-react/mui';
|
|||||||
import { IReduxState } from '../../../app/types';
|
import { IReduxState } from '../../../app/types';
|
||||||
import { IconUsers } from '../../../base/icons/svg';
|
import { IconUsers } from '../../../base/icons/svg';
|
||||||
import Label from '../../../base/label/components/web/Label';
|
import Label from '../../../base/label/components/web/Label';
|
||||||
// eslint-disable-next-line lines-around-comment
|
import Tooltip from '../../../base/tooltip/components/Tooltip';
|
||||||
// @ts-ignore
|
|
||||||
import { Tooltip } from '../../../base/tooltip';
|
|
||||||
import { getVisitorsShortText, iAmVisitor } from '../../functions';
|
import { getVisitorsShortText, iAmVisitor } from '../../functions';
|
||||||
|
|
||||||
const useStyles = makeStyles()(theme => {
|
const useStyles = makeStyles()(theme => {
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
"node_modules",
|
"node_modules",
|
||||||
"react/features/analytics/handlers/GoogleAnalyticsHandler.ts",
|
"react/features/analytics/handlers/GoogleAnalyticsHandler.ts",
|
||||||
"react/features/base/components/participants-pane-list",
|
"react/features/base/components/participants-pane-list",
|
||||||
|
"react/features/base/tooltip",
|
||||||
"react/features/connection-stats",
|
"react/features/connection-stats",
|
||||||
"react/features/desktop-picker",
|
"react/features/desktop-picker",
|
||||||
"react/features/e2ee",
|
"react/features/e2ee",
|
||||||
|
|||||||
Reference in New Issue
Block a user