diff --git a/config.js b/config.js index df8f62afe7..4c822e9bd4 100644 --- a/config.js +++ b/config.js @@ -896,25 +896,55 @@ var config = { If there is no url set or there are missing fields, the defaults are applied. The config file should be in JSON. None of the fields are mandatory and the response must have the shape: - { - // The domain url to apply (will replace the domain in the sharing conference link/embed section) - inviteDomain: 'example-company.org, - // The hex value for the colour used as background - backgroundColor: '#fff', - // The url for the image used as background - backgroundImageUrl: 'https://example.com/background-img.png', - // The anchor url used when clicking the logo image - logoClickUrl: 'https://example-company.org', - // The url used for the image used as logo - logoImageUrl: 'https://example.com/logo-img.png', - // Overwrite for pool of background images for avatars - avatarBackgrounds: ['url(https://example.com/avatar-background-1.png)', '#FFF'], - // The lobby/prejoin screen background - premeetingBackground: 'url(https://example.com/premeeting-background.png)', - // A list of images that can be used as video backgrounds. - // When this field is present, the default images will be replaced with those provided. - virtualBackgrounds: ['https://example.com/img.jpg'] - } + { + // The domain url to apply (will replace the domain in the sharing conference link/embed section) + inviteDomain: 'example-company.org, + // The hex value for the colour used as background + backgroundColor: '#fff', + // The url for the image used as background + backgroundImageUrl: 'https://example.com/background-img.png', + // The anchor url used when clicking the logo image + logoClickUrl: 'https://example-company.org', + // The url used for the image used as logo + logoImageUrl: 'https://example.com/logo-img.png', + // Overwrite for pool of background images for avatars + avatarBackgrounds: ['url(https://example.com/avatar-background-1.png)', '#FFF'], + // The lobby/prejoin screen background + premeetingBackground: 'url(https://example.com/premeeting-background.png)', + // A list of images that can be used as video backgrounds. + // When this field is present, the default images will be replaced with those provided. + virtualBackgrounds: ['https://example.com/img.jpg'], + // Object containing a theme's properties. It also supports partial overwrites of the main theme. + // For a list of all possible theme tokens and their current defaults, please check: + // https://github.com/jitsi/jitsi-meet/tree/master/resources/custom-theme/custom-theme.json + // For a short explanations on each of the tokens, please check: + // https://github.com/jitsi/jitsi-meet/blob/master/react/features/base/ui/Tokens.js + // IMPORTANT!: This is work in progress so many of the various tokens are not yet applied in code + // or they are partially applied. + customTheme: { + palette: { + ui01: "orange !important", + ui02: "maroon", + surface02: 'darkgreen', + ui03: "violet", + ui04: "magenta", + ui05: "blueviolet", + field02Hover: 'red', + action01: 'green', + action01Hover: 'lightgreen', + action02Disabled: 'beige', + success02: 'cadetblue', + action02Hover: 'aliceblue' + }, + typography: { + labelRegular: { + fontSize: 25, + lineHeight: 30, + fontWeight: 500 + } + } + } + } */ // dynamicBrandingUrl: '', diff --git a/css/_toolbars.scss b/css/_toolbars.scss index a726bea6b8..73adea2552 100644 --- a/css/_toolbars.scss +++ b/css/_toolbars.scss @@ -77,14 +77,6 @@ } } -.toolbox-button { - color: $toolbarButtonColor; - cursor: pointer; - display: inline-block; - line-height: $newToolbarSize; - text-align: center; -} - .toolbar-button-with-badge { display: inline-block; position: relative; @@ -115,86 +107,6 @@ padding-bottom: env(safe-area-inset-bottom, 0); } -.toolbox-content-items { - background: $newToolbarBackgroundColor; - border-radius: 6px; - margin: 0 auto; - padding: 6px; - text-align: center; - pointer-events: all; - box-shadow: 0px 2px 8px 4px rgba(0, 0, 0, 0.25), 0px 0px 0px 1px rgba(0, 0, 0, 0.15); - - >div { - margin-left: 8px; - - &:first-child { - margin-left: 0; - } - } -} - -.overflow-menu { - font-size: 14px; - list-style-type: none; - padding: 8px 0; - background-color: $menuBG; - - .profile-text { - max-width: 150px; - text-overflow: ellipsis; - overflow: hidden; - white-space: nowrap; - } -} - -.overflow-menu-item { - align-items: center; - color: $overflowMenuItemColor; - cursor: pointer; - display: flex; - font-size: 14px; - font-weight: 400; - height: 40px; - line-height: 24px; - padding: 8px 16px; - box-sizing: border-box; - - @media (hover: hover) and (pointer: fine) { - &:hover { - background: $overflowMenuItemBackground; - } - } - - div { - display: flex; - flex-direction: row; - align-items: center; - } - - &.unclickable { - cursor: default; - } - &.disabled { - cursor: initial; - color: #929292; - - &:hover { - background: none; - } - - svg { - fill: #929292; - } - - } - - @media (hover: hover) and (pointer: fine) { - &.unclickable:hover { - background: inherit; - } - } -} - .beta-tag { background: #36383C; border-radius: 3px; @@ -205,73 +117,12 @@ text-transform: uppercase; } -.overflow-menu-item-icon { - margin-right: 16px; - - i { - display: inline; - font-size: 24px; - } - - @media (hover: hover) and (pointer: fine) { - i:hover { - background-color: initial; - } - } - - img { - max-width: 24px; - max-height: 24px; - } - - svg { - fill: #fff; - height: 20px; - width: 20px; - } -} - - .overflow-menu-hr { border-top: 1px solid #4C4D50; border-bottom: 0; margin: 8px 0; } -.toolbox-icon { - display: flex; - border-radius: 3px; - flex-direction: column; - font-size: 24px; - height: $newToolbarSize; - justify-content: center; - width: $newToolbarSize; - - @media (hover: hover) and (pointer: fine) { - &:hover { - background: $newToolbarButtonHoverColor; - } - } - - @media (max-width: 320px) { - height: 36px; - width: 36px; - } - - &.toggled { - background: $newToolbarButtonToggleColor; - } - - &.disabled { - cursor: initial !important; - background-color: #36383c !important; - - svg { - fill: #929292 !important; - } - } -} - .hangup-button { background-color: $hangupColor; diff --git a/css/buttons/copy.scss b/css/buttons/copy.scss deleted file mode 100644 index b381c09ae7..0000000000 --- a/css/buttons/copy.scss +++ /dev/null @@ -1,38 +0,0 @@ -.copy-button { - display: flex; - justify-content: space-between; - align-items: center; - padding: 8px 8px 8px 16px; - margin-top: 5px; - width: calc(100% - 24px); - height: 24px; - - background: #0376DA; - border-radius: 4px; - cursor: pointer; - - &:hover { - background: #278ADF; - font-weight: 600; - } - - &-content { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - max-width: 292px; - margin-right: 16px; - - &.selected { - font-weight: 600; - } - } - - &.clicked { - background: #31B76A; - } - - & > div > svg > path { - fill: #fff; - } -} \ No newline at end of file diff --git a/css/main.scss b/css/main.scss index d9ec0d6125..48d30797d7 100644 --- a/css/main.scss +++ b/css/main.scss @@ -33,7 +33,6 @@ $flagsImagePath: "../images/"; @import 'inlay'; @import 'reload_overlay/reload_overlay'; @import 'mini_toolbox'; -@import 'buttons/copy.scss'; @import 'modals/desktop-picker/desktop-picker'; @import 'modals/device-selection/device-selection'; @import 'modals/dialog'; diff --git a/css/premeeting/_premeeting-screens.scss b/css/premeeting/_premeeting-screens.scss index 0601ff77d4..1dba384094 100644 --- a/css/premeeting/_premeeting-screens.scss +++ b/css/premeeting/_premeeting-screens.scss @@ -138,7 +138,6 @@ .toolbox-content-items { background: transparent; - border-radius: 0; box-shadow: none; display: flex; justify-content: space-evenly; diff --git a/react/features/base/buttons/CopyButton.js b/react/features/base/buttons/CopyButton.js index b1d2188cba..88e6ecf1a5 100644 --- a/react/features/base/buttons/CopyButton.js +++ b/react/features/base/buttons/CopyButton.js @@ -2,14 +2,67 @@ /* eslint-disable react/jsx-no-bind */ -import React, { useState } from 'react'; +import { withStyles } from '@material-ui/styles'; +import clsx from 'clsx'; +import React, { useEffect, useState } from 'react'; import { Icon, IconCheck, IconCopy } from '../../base/icons'; +import { withPixelLineHeight } from '../styles/functions.web'; import { copyText } from '../util'; +const styles = theme => { + return { + copyButton: { + ...withPixelLineHeight(theme.typography.bodyLongRegular), + borderRadius: theme.shape.borderRadius, + display: 'flex', + justifyContent: 'space-between', + alignItems: 'center', + padding: '8px 8px 8px 16px', + marginTop: 5, + width: 'calc(100% - 24px)', + height: 24, + + background: theme.palette.action01, + cursor: 'pointer', + + '&:hover': { + backgroundColor: theme.palette.action01Hover, + fontWeight: 600 + }, + + '&.clicked': { + background: theme.palette.success02 + }, + + '& > div > svg > path': { + fill: theme.palette.text01 + } + }, + content: { + overflow: 'hidden', + textOverflow: 'ellipsis', + whiteSpace: 'nowrap', + maxWidth: 292, + marginRight: 16, + + '&.selected': { + fontWeight: 600 + } + } + }; +}; + +let mounted; + type Props = { + /** + * An object containing the CSS classes. + */ + classes: Object, + /** * Css class to apply on container. */ @@ -46,10 +99,18 @@ type Props = { * * @returns {React$Element} */ -function CopyButton({ className, displayedText, textToCopy, textOnHover, textOnCopySuccess, id }: Props) { +function CopyButton({ classes, className, displayedText, textToCopy, textOnHover, textOnCopySuccess, id }: Props) { const [ isClicked, setIsClicked ] = useState(false); const [ isHovered, setIsHovered ] = useState(false); + useEffect(() => { + mounted = true; + + return () => { + mounted = false; + }; + }, []); + /** * Click handler for the element. * @@ -64,7 +125,10 @@ function CopyButton({ className, displayedText, textToCopy, textOnHover, textOnC setIsClicked(true); setTimeout(() => { - setIsClicked(false); + // avoid: Can't perform a React state update on an unmounted component + if (mounted) { + setIsClicked(false); + } }, 2500); } } @@ -112,7 +176,7 @@ function CopyButton({ className, displayedText, textToCopy, textOnHover, textOnC if (isClicked) { return ( <> -
+
{ textOnCopySuccess }
@@ -122,8 +186,8 @@ function CopyButton({ className, displayedText, textToCopy, textOnHover, textOnC return ( <> -
- {isHovered ? textOnHover : displayedText} +
+ { isHovered ? textOnHover : displayedText }
@@ -133,7 +197,7 @@ function CopyButton({ className, displayedText, textToCopy, textOnHover, textOnC return (
{ + return { + actionButton: { + ...withPixelLineHeight(theme.typography.bodyLongBold), + borderRadius: theme.shape.borderRadius, + boxSizing: 'border-box', + color: theme.palette.text01, + cursor: 'pointer', + display: 'inline-block', + marginBottom: '16px', + padding: '7px 16px', + position: 'relative', + textAlign: 'center', + width: '100%', + + '&.primary': { + background: theme.palette.action01, + border: '1px solid #0376DA', + + '&:hover': { + backgroundColor: theme.palette.action01Hover + } + }, + + '&.secondary': { + background: theme.palette.action02, + border: '1px solid transparent' + }, + + '&.text': { + width: 'auto', + fontSize: '13px', + margin: '0', + padding: '0' + }, + + '&.disabled': { + background: theme.palette.action01Disabled, + border: '1px solid #5E6D7A', + color: '#AFB6BC', + cursor: 'initial', + + '.icon': { + '& > svg': { + fill: '#AFB6BC' + } + } + }, + + + [theme.breakpoints.down('400')]: { + fontSize: 16, + marginBottom: 8, + padding: '11px 16px' + } + }, + options: { + borderRadius: theme.shape.borderRadius / 2, + alignItems: 'center', + display: 'flex', + height: '100%', + justifyContent: 'center', + position: 'absolute', + right: 0, + top: 0, + width: 36, + + '&:hover': { + backgroundColor: '#0262B6' + }, + + '& svg': { + pointerEvents: 'none' + } + } + }; +}; + /** * Button used for pre meeting actions. * @@ -85,6 +178,7 @@ type Props = { */ function ActionButton({ children, + classes, className = '', disabled, hasOptions, @@ -115,11 +209,18 @@ function ActionButton({ } }, [ onOptionsClick, disabled ]); + const containerClasses = clsx( + classes.actionButton, + className && className, + type, + disabled && 'disabled' + ); + return (
createStyles({ '@global': { - ...formatCommonClasses(commonStyles), + ...formatCommonClasses(commonStyles(theme)), ...getGlobalStyles(theme) } }) diff --git a/react/features/base/ui/components/JitsiThemeProvider.web.js b/react/features/base/ui/components/JitsiThemeProvider.web.js index 2a98facb2a..8923dfef48 100644 --- a/react/features/base/ui/components/JitsiThemeProvider.web.js +++ b/react/features/base/ui/components/JitsiThemeProvider.web.js @@ -3,10 +3,17 @@ import { ThemeProvider } from '@material-ui/core/styles'; import * as React from 'react'; +import { connect } from '../../../base/redux'; + import BaseTheme from './BaseTheme'; type Props = { + /** + * The default theme or theme set through advanced branding. + */ + _theme: Object, + /** * The children of the component. */ @@ -19,6 +26,22 @@ type Props = { * @param {Object} props - The props of the component. * @returns {React.ReactNode} */ -export default function JitsiThemeProvider(props: Props) { - return { props.children }; +function JitsiThemeProvider(props: Props) { + return { props.children }; } + +/** + * Maps part of the Redux state to the props of this component. + * + * @param {Object} state - The Redux state. + * @returns {Props} + */ +function _mapStateToProps(state) { + const { muiBrandedTheme } = state['features/dynamic-branding']; + + return { + _theme: muiBrandedTheme || BaseTheme + }; +} + +export default connect(_mapStateToProps)(JitsiThemeProvider); diff --git a/react/features/base/ui/constants.js b/react/features/base/ui/constants.js index b5137a999a..76f89997e3 100644 --- a/react/features/base/ui/constants.js +++ b/react/features/base/ui/constants.js @@ -6,19 +6,153 @@ * */ export const commonClassName = { - emptyList: 'empty-list' + emptyList: 'empty-list', + overflowMenuItem: 'overflow-menu-item', + overflowMenuItemIcon: 'overflow-menu-item-icon', + toolboxIcon: 'toolbox-icon', + toolboxButton: 'toolbox-button', + toolboxContentItems: 'toolbox-content-items' }; /** - * An object containing the declaration of the common, reusable CSS classes. + * Returns an object containing the declaration of the common, reusable CSS classes. + * + * @param {Object} theme -The theme. + * + * @returns {Object} - The common styles. */ -export const commonStyles = { - // '.empty-list' - [commonClassName.emptyList]: { - listStyleType: 'none', - margin: 0, - padding: 0 - } +export const commonStyles = (theme: Object) => { + return { + // '.empty-list' + [commonClassName.emptyList]: { + listStyleType: 'none', + margin: 0, + padding: 0 + }, + [commonClassName.overflowMenuItem]: { + alignItems: 'center', + color: theme.palette.text01, + cursor: 'pointer', + display: 'flex', + fontSize: 14, + fontWeight: 400, + height: 40, + lineHeight: '24px', + padding: '8px 16px', + boxSizing: 'border-box', + '& > div': { + display: 'flex', + alignItems: 'center' + }, + + '&.unclickable': { + cursor: 'default' + }, + + '&.disabled': { + cursor: 'initial', + color: theme.palette.text03, + + '&:hover': { + background: 'none' + }, + + '& svg': { + fill: theme.palette.text03 + } + }, + + '@media (hover: hover) and (pointer: fine)': { + '&:hover': { + background: theme.palette.action02Hover + }, + '&.unclickable:hover': { + background: 'inherit' + } + } + }, + [commonClassName.overflowMenuItemIcon]: { + marginRight: '16px', + + '& i': { + display: 'inline', + fontSize: 24 + }, + + '@media (hover: hover) and (pointer: fine)': { + '&i:hover': { + backgroundColor: 'initial' + } + }, + + '& img': { + maxWidth: 24, + maxHeight: 24 + }, + + '& svg': { + fill: theme.palette.text01, + height: 20, + width: 20 + } + }, + [commonClassName.toolboxIcon]: { + display: 'flex', + borderRadius: 3, + flexDirection: 'column', + fontSize: 24, + height: 48, + justifyContent: 'center', + width: 48, + + '@media (hover: hover) and (pointer: fine)': { + '&:hover': { + background: theme.palette.action02Hover + } + }, + [theme.breakpoints.down('320')]: { + height: 36, + width: 36 + }, + + '&.toggled': { + background: theme.palette.ui02 + }, + + '&.disabled': { + cursor: 'initial !important', + backgroundColor: `${theme.palette.action02Disabled} !important`, + + '& svg': { + fill: `${theme.palette.text03} !important` + } + } + }, + [commonClassName.toolboxButton]: { + color: theme.palette.text01, + cursor: 'pointer', + display: 'inline-block', + lineHeight: '48px', + textAlign: 'center' + }, + [commonClassName.toolboxContentItems]: { + background: theme.palette.ui01, + borderRadius: 6, + margin: '0 auto', + padding: 6, + textAlign: 'center', + pointerEvents: 'all', + boxShadow: '0px 2px 8px 4px rgba(0, 0, 0, 0.25), 0px 0px 0px 1px rgba(0, 0, 0, 0.15)', + + '& > div': { + marginLeft: 8, + + '&:first-child': { + marginLeft: 0 + } + } + } + }; }; /** diff --git a/react/features/base/ui/functions.web.js b/react/features/base/ui/functions.web.js index c433b37ce1..27813652bd 100644 --- a/react/features/base/ui/functions.web.js +++ b/react/features/base/ui/functions.web.js @@ -10,7 +10,7 @@ import { createColorTokens } from './utils'; * @param {Object} arg - The ui tokens. * @returns {Object} */ -export function createWebTheme({ font, colors, colorMap, shape, spacing, typography }: Object) { +export function createWebTheme({ font, colors, colorMap, shape, spacing, typography, breakpoints }: Object) { return createMuiTheme({ props: { // disable ripple effect on buttons globally @@ -27,7 +27,8 @@ export function createWebTheme({ font, colors, colorMap, shape, spacing, typogra typography: { font, ...typography - } + }, + breakpoints }); } @@ -46,3 +47,4 @@ export function formatCommonClasses(stylesObj: Object) { return formatted; } + diff --git a/react/features/dynamic-branding/functions.js b/react/features/dynamic-branding/functions.js index f9648efa0c..7fca88a3e4 100644 --- a/react/features/dynamic-branding/functions.js +++ b/react/features/dynamic-branding/functions.js @@ -1,5 +1,9 @@ // @flow +import { createMuiTheme } from '@material-ui/core/styles'; + import { loadConfig } from '../base/lib-jitsi-meet'; +import { font, colors, colorMap, spacing, shape, typography, breakpoints } from '../base/ui/Tokens'; +import { createColorTokens } from '../base/ui/utils'; /** * Extracts the fqn part from a path, where fqn represents @@ -45,3 +49,89 @@ export async function getDynamicBrandingUrl() { export function isDynamicBrandingDataLoaded(state: Object) { return state['features/dynamic-branding'].customizationReady; } + +/** + * Creates MUI branding theme based on the custom theme json. + * + * @param {Object} customTheme - The branded custom theme. + * @returns {Object} - The MUI theme. + */ +export function createMuiBrandingTheme(customTheme: Object) { + const { + palette: customPalette, + shape: customShape, + typography: customTypography, + breakpoints: customBreakpoints, + spacing: customSpacing + } = customTheme; + + const newPalette = createColorTokens(colorMap, colors); + + if (customPalette) { + overwriteRecurrsive(newPalette, customPalette); + } + + const newShape = { ...shape }; + + if (customShape) { + overwriteRecurrsive(newShape, customShape); + } + + const newTypography = { + font: { ...font }, + ...typography + }; + + if (customTypography) { + overwriteRecurrsive(newTypography, customTypography); + } + + const newBreakpoints = { ...breakpoints }; + + if (customBreakpoints) { + overwriteRecurrsive(newBreakpoints, customBreakpoints); + } + + let newSpacing = [ ...spacing ]; + + if (customSpacing && customSpacing.length) { + newSpacing = customSpacing; + } + + return createMuiTheme({ + props: { + // disable ripple effect on buttons globally + MuiButtonBase: { + disableRipple: true + } + }, + + // use token spacing array + spacing: newSpacing + }, { + palette: newPalette, + shape: newShape, + typography: newTypography, + breakpoints: newBreakpoints + }); +} + +/** + * Overwrites recursively values from object 2 into object 1 based on common keys. + * (Merges object2 into object1). + * + * @param {Object} obj1 - The object holding the merged values. + * @param {Object} obj2 - The object to compare to and take values from. + * @returns {void} + */ +function overwriteRecurrsive(obj1: Object, obj2: Object) { + Object.keys(obj2).forEach(key => { + if (obj1.hasOwnProperty(key)) { + if (typeof obj1[key] === 'object') { + overwriteRecurrsive(obj1[key], obj2[key]); + } else { + obj1[key] = obj2[key]; + } + } + }); +} diff --git a/react/features/dynamic-branding/middleware.js b/react/features/dynamic-branding/middleware.js index 184103d0a2..38085c20bc 100644 --- a/react/features/dynamic-branding/middleware.js +++ b/react/features/dynamic-branding/middleware.js @@ -3,7 +3,9 @@ import { APP_WILL_MOUNT } from '../base/app'; import { MiddlewareRegistry } from '../base/redux'; +import { SET_DYNAMIC_BRANDING_DATA } from './actionTypes'; import { fetchCustomBrandingData } from './actions'; +import { createMuiBrandingTheme } from './functions'; MiddlewareRegistry.register(store => next => action => { switch (action.type) { @@ -12,6 +14,13 @@ MiddlewareRegistry.register(store => next => action => { store.dispatch(fetchCustomBrandingData()); break; } + case SET_DYNAMIC_BRANDING_DATA: { + const { customTheme } = action.value; + + if (customTheme) { + action.value.muiBrandedTheme = createMuiBrandingTheme(customTheme); + } + } } return next(action); diff --git a/react/features/dynamic-branding/reducer.js b/react/features/dynamic-branding/reducer.js index 7817ccc4bb..39a67ee532 100644 --- a/react/features/dynamic-branding/reducer.js +++ b/react/features/dynamic-branding/reducer.js @@ -100,6 +100,14 @@ const DEFAULT_STATE = { */ logoImageUrl: '', + /** + * The generated MUI branded theme based on the custom theme json. + * + * @public + * @type {boolean} + */ + muiBrandedTheme: undefined, + /** * The lobby/prejoin background. * @@ -140,6 +148,7 @@ ReducerRegistry.register(STORE_NAME, (state = DEFAULT_STATE, action) => { inviteDomain, logoClickUrl, logoImageUrl, + muiBrandedTheme, premeetingBackground, virtualBackgrounds } = action.value; @@ -153,6 +162,7 @@ ReducerRegistry.register(STORE_NAME, (state = DEFAULT_STATE, action) => { inviteDomain, logoClickUrl, logoImageUrl, + muiBrandedTheme, premeetingBackground, customizationFailed: false, customizationReady: true, diff --git a/react/features/prejoin/components/DropdownButton.js b/react/features/prejoin/components/DropdownButton.js new file mode 100644 index 0000000000..a09c39603e --- /dev/null +++ b/react/features/prejoin/components/DropdownButton.js @@ -0,0 +1,109 @@ +// @flow + +import { withStyles } from '@material-ui/styles'; +import React from 'react'; + +import { Icon } from '../../base/icons'; + +type Props = { + + /** + * The css classes generated from theme. + */ + classes: Object, + + /** + * Attribute used in automated testing. + */ + dataTestId: string, + + /** + * The button's icon. + */ + icon: HTMLElement, + + /** + * The button's label. + */ + label: string, + + /** + * Function to be called when button is clicked. + */ + onButtonClick: Function, + + /** + * Function to be called on key pressed. + */ + onKeyPressed: Function, + + /** + * Used for translation. + */ + t: Function +}; + +/** + * Creates the styles for the component. + * + * @param {Object} theme - The current UI theme. + * + * @returns {Object} + */ +const styles = theme => { + return { + prejoinPreviewDropdownBtn: { + alignItems: 'center', + color: '#1C2025', + cursor: 'pointer', + display: 'flex', + height: 40, + fontSize: 15, + lineHeight: 24, + padding: '0 16px', + backgroundColor: theme.palette.field02, + + '&:hover': { + backgroundColor: theme.palette.field02Hover + } + }, + prejoinPreviewDropdownIcon: { + display: 'inline-block', + marginRight: 16, + + '& > svg': { + fill: '#1C2025' + } + } + }; +}; + +/** + * Buttons used for pre meeting actions. + * + * @returns {ReactElement} + */ +const DropdownButton = ({ + classes, + dataTestId, + icon, + onButtonClick, + onKeyPressed, + label +}: Props) => ( +
+ + {label} +
+); + +export default withStyles(styles)(DropdownButton); diff --git a/react/features/prejoin/components/Prejoin.js b/react/features/prejoin/components/Prejoin.js index 9c83bcefd2..a395c4aa03 100644 --- a/react/features/prejoin/components/Prejoin.js +++ b/react/features/prejoin/components/Prejoin.js @@ -6,7 +6,7 @@ import React, { Component } from 'react'; import { getRoomName } from '../../base/conference'; import { isNameReadOnly } from '../../base/config'; import { translate } from '../../base/i18n'; -import { Icon, IconArrowDown, IconArrowUp, IconPhone, IconVolumeOff } from '../../base/icons'; +import { IconArrowDown, IconArrowUp, IconPhone, IconVolumeOff } from '../../base/icons'; import { isVideoMutedByUser } from '../../base/media'; import { ActionButton, InputField, PreMeetingScreen } from '../../base/premeeting'; import { connect } from '../../base/redux'; @@ -24,6 +24,7 @@ import { isJoinByPhoneDialogVisible } from '../functions'; +import DropdownButton from './DropdownButton'; import JoinByPhoneDialog from './dialogs/JoinByPhoneDialog'; type Props = { @@ -327,32 +328,18 @@ class Prejoin extends Component {
-
- - { t('prejoin.joinWithoutAudio') } -
- {hasJoinByPhoneButton &&
- - { t('prejoin.joinAudioByPhone') } -
} + + {hasJoinByPhoneButton && }
} isOpen = { showJoinByPhoneButtons } onClose = { _onDropdownClose }> diff --git a/react/features/toolbox/components/web/Toolbox.js b/react/features/toolbox/components/web/Toolbox.js index 2eae039370..b4403699f9 100644 --- a/react/features/toolbox/components/web/Toolbox.js +++ b/react/features/toolbox/components/web/Toolbox.js @@ -1,5 +1,6 @@ // @flow +import { withStyles } from '@material-ui/core/styles'; import React, { Component, Fragment } from 'react'; import keyboardShortcut from '../../../../../modules/keyboardshortcut/keyboardshortcut'; @@ -224,6 +225,11 @@ type Props = { */ _virtualSource: Object, + /** + * An object containing the CSS classes. + */ + classes: Object, + /** * Invoked to active other features of the app. */ @@ -248,6 +254,17 @@ type Props = { declare var APP: Object; +const styles = theme => { + return { + overflowMenu: { + fontSize: 14, + listStyleType: 'none', + padding: '8px 0', + backgroundColor: theme.palette.ui03 + } + }; +}; + /** * Implements the conference toolbox on React/Web. * @@ -1200,10 +1217,11 @@ class Toolbox extends Component { const { _isMobile, _overflowMenuVisible, + _reactionsEnabled, _toolbarButtons, + classes, showDominantSpeakerName, - t, - _reactionsEnabled + t } = this.props; const toolbarAccLabel = 'toolbar.accessibilityLabel.moreActionsMenu'; @@ -1240,7 +1258,7 @@ class Toolbox extends Component { }>