diff --git a/custom.d.ts b/custom.d.ts new file mode 100644 index 000000000..60bd434c6 --- /dev/null +++ b/custom.d.ts @@ -0,0 +1,4 @@ +declare module '*.svg' { + const content: any; + export default content; +} diff --git a/react/.eslintrc.js b/react/.eslintrc.js index ceb016626..50a45456b 100644 --- a/react/.eslintrc.js +++ b/react/.eslintrc.js @@ -17,7 +17,9 @@ module.exports = { '@typescript-eslint/no-empty-function': 'off', '@typescript-eslint/ban-types': 'off', '@typescript-eslint/no-explicit-any': 'off', - 'no-prototype-builtins': 'off' + 'no-prototype-builtins': 'off', + 'no-shadow': 'off', + '@typescript-eslint/no-shadow': [ 'error' ] }, 'plugins': [ '@typescript-eslint' ], 'extends': [ diff --git a/react/features/base/components/common/Button.tsx b/react/features/base/components/common/Button.tsx new file mode 100644 index 000000000..cc16c68ea --- /dev/null +++ b/react/features/base/components/common/Button.tsx @@ -0,0 +1,195 @@ +import { makeStyles } from '@material-ui/core'; +import clsx from 'clsx'; +import React from 'react'; + +import Icon from '../../icons/components/Icon'; +import { BUTTON_TYPES } from '../../react/constants'; +import { withPixelLineHeight } from '../../styles/functions.web'; + +import { ButtonProps } from './types'; + +interface IButtonProps extends ButtonProps { + + /** + * Whether or not the button should be full width. + */ + fullWidth?: boolean, + + /** + * The id of the button. + */ + id?: string; + + /** + * Click callback. + */ + onClick: () => void; + + /** + * Which size the button should be. + */ + size?: 'small' | 'medium' | 'large'; +} + +const useStyles = makeStyles((theme: any) => { + return { + button: { + backgroundColor: theme.palette.action01, + color: theme.palette.text01, + borderRadius: theme.shape.borderRadius, + padding: '10px 16px', + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + border: 0, + ...withPixelLineHeight(theme.typography.bodyShortBold), + transition: 'background .2s', + cursor: 'pointer', + + '&:hover': { + backgroundColor: theme.palette.action01Hover + }, + + '&:active': { + backgroundColor: theme.palette.action01Active + }, + + '&:focus': { + outline: 0, + boxShadow: `0px 0px 0px 2px ${theme.palette.focus01}` + }, + + '& svg': { + fill: theme.palette.icon01 + } + }, + + primary: {}, + + secondary: { + backgroundColor: theme.palette.action02, + color: theme.palette.text04, + + '&:hover': { + backgroundColor: theme.palette.action02Hover + }, + + '&:active': { + backgroundColor: theme.palette.action02Active + }, + + '& svg': { + fill: theme.palette.icon04 + } + }, + + tertiary: { + backgroundColor: theme.palette.action03, + + '&:hover': { + backgroundColor: theme.palette.action03Hover + }, + + '&:active': { + backgroundColor: theme.palette.action03Active + } + }, + + destructive: { + backgroundColor: theme.palette.actionDanger, + + '&:hover': { + backgroundColor: theme.palette.actionDangerHover + }, + + '&:active': { + backgroundColor: theme.palette.actionDangerActive + } + }, + + disabled: { + backgroundColor: theme.palette.disabled01, + color: theme.palette.text03, + + '&:hover': { + backgroundColor: theme.palette.disabled01, + color: theme.palette.text03 + }, + + '&:active': { + backgroundColor: theme.palette.disabled01, + color: theme.palette.text03 + }, + + '& svg': { + fill: theme.palette.icon03 + } + }, + + iconButton: { + padding: '10px' + }, + + textWithIcon: { + marginLeft: `${theme.spacing(2)}px` + }, + + small: { + padding: '8px 16px', + ...withPixelLineHeight(theme.typography.labelBold), + + '&.iconButton': { + padding: '6px' + } + }, + + medium: {}, + + large: { + padding: '13px 16px', + ...withPixelLineHeight(theme.typography.bodyShortBoldLarge), + + '&.iconButton': { + padding: '14px' + } + }, + + fullWidth: { + width: '100%' + } + }; +}); + +const Button = ({ + accessibilityLabel, + disabled, + fullWidth, + icon, + id, + label, + onClick, + size = 'medium', + type = BUTTON_TYPES.PRIMARY +}: IButtonProps) => { + const styles = useStyles(); + + return ( + + ); +}; + +export default Button; diff --git a/react/features/base/components/common/types.ts b/react/features/base/components/common/types.ts new file mode 100644 index 000000000..43b4abb15 --- /dev/null +++ b/react/features/base/components/common/types.ts @@ -0,0 +1,29 @@ +import { BUTTON_TYPES } from '../../react/constants'; + +export interface ButtonProps { + + /** + * Label used for accessibility. + */ + accessibilityLabel: string; + + /** + * Whether or not the button is disabled. + */ + disabled?: boolean; + + /** + * The icon to be displayed on the button. + */ + icon?: Function; + + /** + * The text to be displayed on the button. + */ + label?: string; + + /** + * The type of button to be displayed. + */ + type?: BUTTON_TYPES; +} diff --git a/react/features/base/icons/components/Icon.js b/react/features/base/icons/components/Icon.tsx similarity index 96% rename from react/features/base/icons/components/Icon.js rename to react/features/base/icons/components/Icon.tsx index 85a5c5530..8743fb8e6 100644 --- a/react/features/base/icons/components/Icon.js +++ b/react/features/base/icons/components/Icon.tsx @@ -1,8 +1,10 @@ -// @flow - +/* eslint-disable import/order */ import React, { useCallback } from 'react'; +// @ts-ignore import { Container } from '../../react/base'; + +// @ts-ignore import { styleTypeToObject } from '../../styles'; type Props = { @@ -15,7 +17,7 @@ type Props = { /** * Color of the icon (if not provided by the style object). */ - color?: ?string, + color?: string, /** * Id prop (mainly for autotests). @@ -35,7 +37,7 @@ type Props = { /** * The size of the icon (if not provided by the style object). */ - size?: ?number | string, + size?: number | string, /** * The preloaded icon component to render. @@ -82,12 +84,12 @@ type Props = { */ ariaControls?: string, - /** + /** * TabIndex for the Icon. */ tabIndex?: number, - /** + /** * Role for the Icon. */ role?: string, @@ -110,7 +112,7 @@ export const DEFAULT_SIZE = navigator.product === 'ReactNative' ? 36 : 22; * Implements an Icon component that takes a loaded SVG file as prop and renders it as an icon. * * @param {Props} props - The props of the component. - * @returns {Reactelement} + * @returns {ReactElement} */ export default function Icon(props: Props) { const { diff --git a/react/features/base/icons/svg/index.js b/react/features/base/icons/svg/index.ts similarity index 99% rename from react/features/base/icons/svg/index.js rename to react/features/base/icons/svg/index.ts index e2e7c7b61..c57991553 100644 --- a/react/features/base/icons/svg/index.js +++ b/react/features/base/icons/svg/index.ts @@ -1,5 +1,3 @@ -// @flow - export { default as IconAdd } from './add.svg'; export { default as IconAddPeople } from './link.svg'; export { default as IconArrowBack } from './arrow_back.svg'; diff --git a/react/features/base/react/components/native/Button.tsx b/react/features/base/react/components/native/Button.tsx index c4659a598..0bfb7d076 100644 --- a/react/features/base/react/components/native/Button.tsx +++ b/react/features/base/react/components/native/Button.tsx @@ -11,13 +11,13 @@ import { import BaseTheme from '../../../ui/components/BaseTheme.native'; // @ts-ignore import { BUTTON_MODES, BUTTON_TYPES } from '../../constants'; -import { ButtonProps } from '../../types'; +import { IButtonProps } from '../../types'; // @ts-ignore import styles from './styles'; -const Button: React.FC = ({ +const Button: React.FC = ({ accessibilityLabel, color: buttonColor, disabled, @@ -27,7 +27,7 @@ const Button: React.FC = ({ onPress, style, type -}: ButtonProps) => { +}: IButtonProps) => { const { t } = useTranslation(); const { CONTAINED } = BUTTON_MODES; const { DESTRUCTIVE, PRIMARY, SECONDARY, TERTIARY } = BUTTON_TYPES; diff --git a/react/features/base/react/constants.js b/react/features/base/react/constants.ts similarity index 67% rename from react/features/base/react/constants.js rename to react/features/base/react/constants.ts index 4201f0796..85d17002f 100644 --- a/react/features/base/react/constants.js +++ b/react/features/base/react/constants.ts @@ -1,5 +1,3 @@ -// @flow - /** * Z-index for components that are to be rendered like an overlay, to be over * everything, such as modal-type of components, or dialogs. @@ -9,12 +7,12 @@ export const OVERLAY_Z_INDEX = 1000; /** * The types of the buttons. */ -export const BUTTON_TYPES = { - PRIMARY: 'primary', - SECONDARY: 'secondary', - TERTIARY: 'tertiary', - DESTRUCTIVE: 'destructive' -}; +export enum BUTTON_TYPES { + PRIMARY = 'primary', + SECONDARY = 'secondary', + TERTIARY = 'tertiary', + DESTRUCTIVE = 'destructive' +} /** * The modes of the buttons. diff --git a/react/features/base/react/types.ts b/react/features/base/react/types.ts index 3bd5e2e62..93a25cf71 100644 --- a/react/features/base/react/types.ts +++ b/react/features/base/react/types.ts @@ -1,13 +1,10 @@ -export interface ButtonProps { - accessibilityLabel?: string; +import { ButtonProps } from '../components/common/types'; + +export interface IButtonProps extends ButtonProps { color?: string; - disabled?: boolean; - icon?: JSX.Element; - label?: string; labelStyle?: Object|undefined; onPress?: Function; style?: Object|undefined; - type?: string; } export interface IconButtonProps { diff --git a/react/features/base/styles/functions.web.js b/react/features/base/styles/functions.web.ts similarity index 86% rename from react/features/base/styles/functions.web.js rename to react/features/base/styles/functions.web.ts index cdd676083..f4a2233b2 100644 --- a/react/features/base/styles/functions.web.js +++ b/react/features/base/styles/functions.web.ts @@ -1,7 +1,7 @@ -// @flow - -import { type StyleType } from './functions.any'; +// @ts-ignore +import { StyleType } from './functions.any'; +// @ts-ignore export * from './functions.any'; /** @@ -32,7 +32,7 @@ export function getFixedPlatformStyle(style: StyleType): StyleType { * @param {Object} base - The base object containing the `lineHeight` property. * @returns {Object} */ -export function withPixelLineHeight(base: Object): Object { +export function withPixelLineHeight(base: any): Object { return { ...base, lineHeight: `${base.lineHeight}px` diff --git a/react/features/participants-pane/components/breakout-rooms/components/web/AddBreakoutRoomButton.js b/react/features/participants-pane/components/breakout-rooms/components/web/AddBreakoutRoomButton.tsx similarity index 51% rename from react/features/participants-pane/components/breakout-rooms/components/web/AddBreakoutRoomButton.js rename to react/features/participants-pane/components/breakout-rooms/components/web/AddBreakoutRoomButton.tsx index 8164c247d..e5d194cd9 100644 --- a/react/features/participants-pane/components/breakout-rooms/components/web/AddBreakoutRoomButton.js +++ b/react/features/participants-pane/components/breakout-rooms/components/web/AddBreakoutRoomButton.tsx @@ -1,36 +1,27 @@ -// @flow - -import { makeStyles } from '@material-ui/core/styles'; +/* eslint-disable lines-around-comment */ import React, { useCallback } from 'react'; import { useTranslation } from 'react-i18next'; import { useDispatch } from 'react-redux'; +import Button from '../../../../../base/components/common/Button'; +import { BUTTON_TYPES } from '../../../../../base/react/constants'; +// @ts-ignore import { createBreakoutRoom } from '../../../../../breakout-rooms/actions'; -import ParticipantPaneBaseButton from '../../../web/ParticipantPaneBaseButton'; - -const useStyles = makeStyles(() => { - return { - button: { - width: '100%' - } - }; -}); export const AddBreakoutRoomButton = () => { const { t } = useTranslation(); const dispatch = useDispatch(); - const styles = useStyles(); const onAdd = useCallback(() => dispatch(createBreakoutRoom()) , [ dispatch ]); return ( - - {t('breakoutRooms.actions.add')} - + fullWidth = { true } + label = { t('breakoutRooms.actions.add') } + onClick = { onAdd } + type = { BUTTON_TYPES.SECONDARY } /> ); }; diff --git a/react/features/participants-pane/components/breakout-rooms/components/web/AutoAssignButton.js b/react/features/participants-pane/components/breakout-rooms/components/web/AutoAssignButton.js deleted file mode 100644 index 572bc63f4..000000000 --- a/react/features/participants-pane/components/breakout-rooms/components/web/AutoAssignButton.js +++ /dev/null @@ -1,42 +0,0 @@ -// @flow - -import { makeStyles } from '@material-ui/styles'; -import React, { useCallback } from 'react'; -import { useTranslation } from 'react-i18next'; -import { useDispatch } from 'react-redux'; - -import { autoAssignToBreakoutRooms } from '../../../../../breakout-rooms/actions'; -import ParticipantPaneBaseButton from '../../../web/ParticipantPaneBaseButton'; - -const useStyles = makeStyles(theme => { - return { - button: { - color: theme.palette.link01, - width: '100%', - backgroundColor: 'transparent', - - '&:hover': { - backgroundColor: 'transparent' - } - } - }; -}); - -export const AutoAssignButton = () => { - const { t } = useTranslation(); - const dispatch = useDispatch(); - const styles = useStyles(); - - const onAutoAssign = useCallback(() => { - dispatch(autoAssignToBreakoutRooms()); - }, [ dispatch ]); - - return ( - - {t('breakoutRooms.actions.autoAssign')} - - ); -}; diff --git a/react/features/participants-pane/components/breakout-rooms/components/web/AutoAssignButton.tsx b/react/features/participants-pane/components/breakout-rooms/components/web/AutoAssignButton.tsx new file mode 100644 index 000000000..8f9cc3b00 --- /dev/null +++ b/react/features/participants-pane/components/breakout-rooms/components/web/AutoAssignButton.tsx @@ -0,0 +1,27 @@ +/* eslint-disable lines-around-comment */ +import React, { useCallback } from 'react'; +import { useTranslation } from 'react-i18next'; +import { useDispatch } from 'react-redux'; + +import Button from '../../../../../base/components/common/Button'; +import { BUTTON_TYPES } from '../../../../../base/react/constants'; +// @ts-ignore +import { autoAssignToBreakoutRooms } from '../../../../../breakout-rooms/actions'; + +export const AutoAssignButton = () => { + const { t } = useTranslation(); + const dispatch = useDispatch(); + + const onAutoAssign = useCallback(() => { + dispatch(autoAssignToBreakoutRooms()); + }, [ dispatch ]); + + return ( + - ); -}; - -export default ParticipantPaneBaseButton; diff --git a/react/features/participants-pane/components/web/ParticipantsPane.js b/react/features/participants-pane/components/web/ParticipantsPane.js index 2e8b99437..f697ea0a3 100644 --- a/react/features/participants-pane/components/web/ParticipantsPane.js +++ b/react/features/participants-pane/components/web/ParticipantsPane.js @@ -3,11 +3,13 @@ import { withStyles } from '@material-ui/core'; import React, { Component } from 'react'; +import Button from '../../../base/components/common/Button'; import participantsPaneTheme from '../../../base/components/themes/participantsPaneTheme.json'; import { openDialog } from '../../../base/dialog'; import { translate } from '../../../base/i18n'; import { Icon, IconClose, IconHorizontalPoints } from '../../../base/icons'; import { isLocalParticipantModerator } from '../../../base/participants/functions'; +import { BUTTON_TYPES } from '../../../base/react/constants'; import { connect } from '../../../base/redux'; import { isAddBreakoutRoomButtonVisible } from '../../../breakout-rooms/functions'; import { MuteEveryoneDialog } from '../../../video-menu/components/'; @@ -21,7 +23,6 @@ import { import { AddBreakoutRoomButton } from '../breakout-rooms/components/web/AddBreakoutRoomButton'; import { RoomList } from '../breakout-rooms/components/web/RoomList'; -import FooterButton from './FooterButton'; import { FooterContextMenu } from './FooterContextMenu'; import LobbyParticipants from './LobbyParticipants'; import MeetingParticipants from './MeetingParticipants'; @@ -258,21 +259,20 @@ class ParticipantsPane extends Component { {_showFooter && (
{_showMuteAllButton && ( - - {t('participantsPane.actions.muteAll')} - + label = { t('participantsPane.actions.muteAll') } + onClick = { this._onMuteAll } + type = { BUTTON_TYPES.SECONDARY } /> )} {_showMoreActionsButton && (
- - - + onClick = { this._onToggleContext } + type = { BUTTON_TYPES.SECONDARY } />