feat(ui-components) Add Select component (#12182)

Remove @atlaskit/dropdown
Convert some files to TS
This commit is contained in:
Robert Pintilii 2022-09-15 15:20:11 +03:00 committed by GitHub
parent f5e60a7ca4
commit 2d6e181a13
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 399 additions and 491 deletions

50
package-lock.json generated
View File

@ -11,7 +11,6 @@
"license": "Apache-2.0",
"dependencies": {
"@amplitude/react-native": "2.7.0",
"@atlaskit/dropdown-menu": "10.1.2",
"@atlaskit/field-text-area": "8.0.4",
"@atlaskit/flag": "14.1.0",
"@atlaskit/icon": "21.2.0",
@ -372,28 +371,6 @@
"@babel/runtime": "^7.0.0"
}
},
"node_modules/@atlaskit/dropdown-menu": {
"version": "10.1.2",
"resolved": "https://registry.npmjs.org/@atlaskit/dropdown-menu/-/dropdown-menu-10.1.2.tgz",
"integrity": "sha512-1E03eM0aoXxwzTzRamnRrBsC9OX1mU2YATfDb45RZRmz+kOx0bw1q5xGXLQsHpUPZFgfK8rdFy8h94qvrucKsQ==",
"dependencies": {
"@atlaskit/analytics-next": "^8.0.0",
"@atlaskit/button": "^15.1.0",
"@atlaskit/droplist": "^11.0.0",
"@atlaskit/icon": "^21.1.0",
"@atlaskit/item": "^12.0.0",
"@atlaskit/theme": "^11.0.0",
"@babel/runtime": "^7.0.0",
"array-find": "^1.0.0",
"prop-types": "^15.5.10",
"react-uid": "^2.2.0"
},
"peerDependencies": {
"react": "^16.8.0",
"react-dom": "^16.8.0",
"styled-components": "^3.2.6"
}
},
"node_modules/@atlaskit/droplist": {
"version": "11.0.9",
"resolved": "https://registry.npmjs.org/@atlaskit/droplist/-/droplist-11.0.9.tgz",
@ -7315,11 +7292,6 @@
"node": ">=0.10.0"
}
},
"node_modules/array-find": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/array-find/-/array-find-1.0.0.tgz",
"integrity": "sha1-bI4obRHtdoMn+OYuzuhzU8o+eLg="
},
"node_modules/array-flatten": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz",
@ -20766,23 +20738,6 @@
"@babel/runtime": "^7.0.0"
}
},
"@atlaskit/dropdown-menu": {
"version": "10.1.2",
"resolved": "https://registry.npmjs.org/@atlaskit/dropdown-menu/-/dropdown-menu-10.1.2.tgz",
"integrity": "sha512-1E03eM0aoXxwzTzRamnRrBsC9OX1mU2YATfDb45RZRmz+kOx0bw1q5xGXLQsHpUPZFgfK8rdFy8h94qvrucKsQ==",
"requires": {
"@atlaskit/analytics-next": "^8.0.0",
"@atlaskit/button": "^15.1.0",
"@atlaskit/droplist": "^11.0.0",
"@atlaskit/icon": "^21.1.0",
"@atlaskit/item": "^12.0.0",
"@atlaskit/theme": "^11.0.0",
"@babel/runtime": "^7.0.0",
"array-find": "^1.0.0",
"prop-types": "^15.5.10",
"react-uid": "^2.2.0"
}
},
"@atlaskit/droplist": {
"version": "11.0.9",
"resolved": "https://registry.npmjs.org/@atlaskit/droplist/-/droplist-11.0.9.tgz",
@ -25888,11 +25843,6 @@
"resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz",
"integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ="
},
"array-find": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/array-find/-/array-find-1.0.0.tgz",
"integrity": "sha1-bI4obRHtdoMn+OYuzuhzU8o+eLg="
},
"array-flatten": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz",

View File

@ -16,7 +16,6 @@
"readmeFilename": "README.md",
"dependencies": {
"@amplitude/react-native": "2.7.0",
"@atlaskit/dropdown-menu": "10.1.2",
"@atlaskit/field-text-area": "8.0.4",
"@atlaskit/flag": "14.1.0",
"@atlaskit/icon": "21.2.0",

View File

@ -1,5 +1,3 @@
// @flow
import { Component } from 'react';
/**
@ -10,17 +8,17 @@ export type Props = {
/**
* Function that closes the dialog.
*/
closeDialog: Function,
closeDialog: Function;
/**
* Callback to invoke on change.
*/
onTabStateChange: Function,
onTabStateChange: Function;
/**
* The id of the tab.
*/
tabId: number
tabId: number;
};
@ -29,7 +27,7 @@ export type Props = {
*
* @augments Component
*/
class AbstractDialogTab<P: Props, S: *> extends Component<P, S> {
class AbstractDialogTab<P extends Props, S> extends Component<P, S> {
/**
* Initializes a new {@code AbstractDialogTab} instance.
*
@ -43,8 +41,6 @@ class AbstractDialogTab<P: Props, S: *> extends Component<P, S> {
this._onChange = this._onChange.bind(this);
}
_onChange: (Object) => void;
/**
* Uses the onTabStateChange function to pass the changed state of the
* controlled tab component to the controlling DialogWithTabs component.
@ -53,7 +49,7 @@ class AbstractDialogTab<P: Props, S: *> extends Component<P, S> {
* value.
* @returns {void}
*/
_onChange(change) {
_onChange(change: Object) {
const { onTabStateChange, tabId } = this.props;
onTabStateChange(tabId, {

View File

@ -1,3 +1,3 @@
<svg width="10" height="6" viewBox="0 0 10 6" fill="none" xmlns="http://www.w3.org/2000/svg">
<svg width="10" height="6" viewBox="0 0 10 6" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.07001 0.248238C8.3471 -0.0596449 8.82132 -0.0846038 9.1292 0.192491C9.43709 0.469585 9.46205 0.943802 9.18495 1.25168L5.65622 5.19348C5.35829 5.52451 4.83922 5.52451 4.54128 5.19348L1.06752 1.25168C0.79043 0.943802 0.81539 0.469585 1.12327 0.192491C1.43115 -0.0846038 1.90537 -0.0596449 2.18247 0.248238L5.09875 3.57062L8.07001 0.248238Z"/>
</svg>

Before

Width:  |  Height:  |  Size: 494 B

After

Width:  |  Height:  |  Size: 482 B

View File

@ -0,0 +1,179 @@
import { Theme } from '@mui/material';
import React, { ChangeEvent } from 'react';
import { makeStyles } from 'tss-react/mui';
import { isMobileBrowser } from '../../../environment/utils';
import Icon from '../../../icons/components/Icon';
import { IconArrowDown } from '../../../icons/svg';
import { withPixelLineHeight } from '../../../styles/functions.web';
interface SelectProps {
/**
* Helper text to be displayed below the select.
*/
bottomLabel?: string;
/**
* Class name for additional styles.
*/
className?: string;
/**
* Wether or not the select is disabled.
*/
disabled?: boolean;
/**
* Wether or not the select is in the error state.
*/
error?: boolean;
/**
* Label to be displayed above the select.
*/
label?: string;
/**
* Change handler.
*/
onChange: (e: ChangeEvent<HTMLSelectElement>) => void;
/**
* The options of the select.
*/
options: Array<{
label: string;
value: number | string;
}>;
/**
* The value of the select.
*/
value: number | string;
}
const useStyles = makeStyles()((theme: Theme) => {
return {
container: {
display: 'flex',
flexDirection: 'column'
},
label: {
color: theme.palette.text01,
...withPixelLineHeight(theme.typography.bodyShortRegular),
marginBottom: theme.spacing(2),
'&.is-mobile': {
...withPixelLineHeight(theme.typography.bodyShortRegularLarge)
}
},
selectContainer: {
position: 'relative'
},
select: {
backgroundColor: theme.palette.ui03,
borderRadius: `${theme.shape.borderRadius}px`,
width: '100%',
...withPixelLineHeight(theme.typography.bodyShortRegular),
color: theme.palette.text01,
padding: '8px 16px',
paddingRight: '42px',
border: 0,
appearance: 'none',
overflow: 'hidden',
whiteSpace: 'nowrap',
textOverflow: 'ellipsis',
'&:focus': {
outline: 0,
boxShadow: `0px 0px 0px 2px ${theme.palette.focus01}`
},
'&:disabled': {
color: theme.palette.text03
},
'&.is-mobile': {
...withPixelLineHeight(theme.typography.bodyShortRegularLarge),
padding: '12px 16px',
paddingRight: '46px'
},
'&.error': {
boxShadow: `0px 0px 0px 2px ${theme.palette.textError}`
}
},
icon: {
position: 'absolute',
top: '8px',
right: '8px',
pointerEvents: 'none',
'&.is-mobile': {
top: '12px',
right: '12px'
}
},
bottomLabel: {
marginTop: theme.spacing(2),
...withPixelLineHeight(theme.typography.labelRegular),
color: theme.palette.text02,
'&.is-mobile': {
...withPixelLineHeight(theme.typography.bodyShortRegular)
},
'&.error': {
color: theme.palette.textError
}
}
};
});
const Select = ({
bottomLabel,
className,
disabled,
error,
label,
onChange,
options,
value }: SelectProps) => {
const { classes, cx, theme } = useStyles();
const isMobile = isMobileBrowser();
return (
<div className = { classes.container }>
{label && <span className = { cx(classes.label, isMobile && 'is-mobile') }>{label}</span>}
<div className = { classes.selectContainer }>
<select
className = { cx(classes.select, isMobile && 'is-mobile', className, error && 'error') }
disabled = { disabled }
onChange = { onChange }
value = { value }>
{options.map(option => (<option
key = { option.value }
value = { option.value }>{option.label}</option>))}
</select>
<Icon
className = { cx(classes.icon, isMobile && 'is-mobile') }
color = { disabled ? theme.palette.icon03 : theme.palette.icon01 }
size = { 22 }
src = { IconArrowDown } />
</div>
{bottomLabel && (
<span className = { cx(classes.bottomLabel, isMobile && 'is-mobile', error && 'error') }>
{bottomLabel}
</span>
)}
</div>
);
};
export default Select;

View File

@ -1,12 +1,8 @@
/* @flow */
import DropdownMenu, {
DropdownItem,
DropdownItemGroup
} from '@atlaskit/dropdown-menu';
import React, { Component } from 'react';
import { translate } from '../../base/i18n/functions';
import Select from '../../base/ui/components/web/Select';
/**
* The type of the React {@code Component} props of {@link DeviceSelector}.
@ -76,7 +72,6 @@ class DeviceSelector extends Component<Props> {
super(props);
this._onSelect = this._onSelect.bind(this);
this._createDropdownItem = this._createDropdownItem.bind(this);
}
/**
@ -98,7 +93,12 @@ class DeviceSelector extends Component<Props> {
return this._renderNoDevices();
}
const items = this.props.devices.map(this._createDropdownItem);
const items = this.props.devices.map(device => {
return {
value: device.deviceId,
label: device.label || device.deviceId
};
});
const defaultSelected = this.props.devices.find(item =>
item.deviceId === this.props.selectedDeviceId
);
@ -130,28 +130,6 @@ class DeviceSelector extends Component<Props> {
);
}
_createDropdownItem: (Object) => void;
/**
* Creates an object in the format expected by AKDropdownMenu for an option.
*
* @param {MediaDeviceInfo} device - An object with a label and a deviceId.
* @private
* @returns {Object} The passed in media device description converted to a
* format recognized as a valid AKDropdownMenu item.
*/
_createDropdownItem(device) {
return (
<DropdownItem
data-deviceid = { device.deviceId }
isSelected = { device.deviceId === this.props.selectedDeviceId }
key = { device.deviceId }
onClick = { this._onSelect }>
{ device.label || device.deviceId }
</DropdownItem>
);
}
/**
* Creates a AKDropdownMenu Component using passed in props and options. If
* the dropdown needs to be disabled, then only the AKDropdownMenu trigger
@ -184,18 +162,10 @@ class DeviceSelector extends Component<Props> {
return (
<div className = 'dropdown-menu'>
<DropdownMenu
shouldFitContainer = { true }
trigger = { triggerText }
triggerButtonProps = {{
shouldFitContainer: true,
id: this.props.id
}}
triggerType = 'button'>
<DropdownItemGroup>
{ options.items }
</DropdownItemGroup>
</DropdownMenu>
<Select
onChange = { this._onSelect }
options = { options.items }
value = { this.props.selectedDeviceId } />
</div>
);
}
@ -211,7 +181,7 @@ class DeviceSelector extends Component<Props> {
* @returns {void}
*/
_onSelect(e) {
const deviceId = e.currentTarget.getAttribute('data-deviceid');
const deviceId = e.target.value;
if (this.props.selectedDeviceId !== deviceId) {
this.props.onSelect(deviceId);

View File

@ -1,196 +0,0 @@
/* @flow */
import {
DropdownItem,
DropdownItemGroup,
DropdownMenuStateless
} from '@atlaskit/dropdown-menu';
import React, { PureComponent } from 'react';
import { translate } from '../../../../base/i18n';
import { YOUTUBE_LIVE_DASHBOARD_URL } from '../constants';
/**
* The type of the React {@code Component} props of {@link StreamKeyPicker}.
*/
type Props = {
/**
* Broadcasts available for selection. Each broadcast item should be an
* object with a title for display in the dropdown and a boundStreamID to
* return in the {@link onBroadcastSelected} callback.
*/
broadcasts: Array<Object>,
/**
* Callback invoked when an item in the dropdown is selected. The selected
* broadcast's boundStreamID will be passed back.
*/
onBroadcastSelected: Function,
/**
* The boundStreamID of the broadcast that should display as selected in the
* dropdown.
*/
selectedBoundStreamID: string,
/**
* Invoked to obtain translated strings.
*/
t: Function
};
/**
* The type of the React {@code Component} state of {@link StreamKeyPicker}.
*/
type State = {
/**
* Whether or not to display the dropdown menu to pick a YouTube broadcast.
*/
isDropdownOpen: boolean
};
/**
* A dropdown to select a YouTube broadcast.
*
* @augments Component
*/
class StreamKeyPicker extends PureComponent<Props, State> {
/**
* Default values for {@code StreamKeyForm} component's properties.
*
* @static
*/
static defaultProps = {
broadcasts: []
};
/**
* The initial state of a {@code StreamKeyForm} instance.
*/
state = {
isDropdownOpen: false
};
/**
* Initializes a new {@code StreamKeyPicker} instance.
*
* @param {Props} props - The React {@code Component} props to initialize
* the new {@code StreamKeyPicker} instance with.
*/
constructor(props: Props) {
super(props);
// Bind event handlers so they are only bound once per instance.
this._onDropdownOpenChange = this._onDropdownOpenChange.bind(this);
this._onSelect = this._onSelect.bind(this);
}
/**
* Implements React's {@link Component#render()}.
*
* @inheritdoc
* @returns {ReactElement}
*/
render() {
const { broadcasts, selectedBoundStreamID, t } = this.props;
if (!broadcasts.length) {
return (
<a
className = 'warning-text'
href = { YOUTUBE_LIVE_DASHBOARD_URL }
rel = 'noopener noreferrer'
target = '_blank'>
{ t('liveStreaming.getStreamKeyManually') }
</a>
);
}
const dropdownItems
= broadcasts.map(broadcast => (
<DropdownItem
data-streamid = { broadcast.boundStreamID }
key = { broadcast.boundStreamID }
onClick = { this._onSelect }>
{ broadcast.title }
</DropdownItem>));
const selected
= this.props.broadcasts.find(
broadcast => broadcast.boundStreamID === selectedBoundStreamID);
const triggerText
= (selected && selected.title) || t('liveStreaming.choose');
return (
<div className = 'broadcast-dropdown dropdown-menu'>
<DropdownMenuStateless
isOpen = { this.state.isDropdownOpen }
onItemActivated = { this._onSelect }
onOpenChange = { this._onDropdownOpenChange }
shouldFitContainer = { true }
trigger = { triggerText }
triggerButtonProps = {{
className: 'broadcast-dropdown-trigger',
shouldFitContainer: true
}}
triggerType = 'button'>
<DropdownItemGroup>
{ dropdownItems }
</DropdownItemGroup>
</DropdownMenuStateless>
</div>
);
}
/**
* Transforms the passed in broadcasts into an array of objects that can
* be parsed by {@code DropdownMenuStateless}.
*
* @param {Array<Object>} broadcasts - The YouTube broadcasts to display.
* @private
* @returns {Array<Object>}
*/
_formatBroadcasts(broadcasts) {
return broadcasts.map(broadcast => {
return {
content: broadcast.title,
value: broadcast
};
});
}
_onDropdownOpenChange: (Object) => void;
/**
* Sets the dropdown to be displayed or not based on the passed in event.
*
* @param {Object} dropdownEvent - The event passed from
* {@code DropdownMenuStateless} indicating if the dropdown should be open
* or closed.
* @private
* @returns {void}
*/
_onDropdownOpenChange(dropdownEvent) {
this.setState({
isDropdownOpen: dropdownEvent.isOpen
});
}
_onSelect: (string) => void;
/**
* Callback invoked when an item has been clicked in the dropdown menu.
*
* @param {Object} e - The key event to handle.
*
* @returns {void}
*/
_onSelect(e) {
const streamId = e.currentTarget.getAttribute('data-streamid');
this.props.onBroadcastSelected(streamId);
}
}
export default translate(StreamKeyPicker);

View File

@ -0,0 +1,125 @@
import React, { PureComponent } from 'react';
import { WithTranslation } from 'react-i18next';
import { translate } from '../../../../base/i18n/functions';
import Select from '../../../../base/ui/components/web/Select';
import { YOUTUBE_LIVE_DASHBOARD_URL } from '../constants';
/**
* The type of the React {@code Component} props of {@link StreamKeyPicker}.
*/
interface Props extends WithTranslation {
/**
* Broadcasts available for selection. Each broadcast item should be an
* object with a title for display in the dropdown and a boundStreamID to
* return in the {@link onBroadcastSelected} callback.
*/
broadcasts: Array<{
boundStreamID: string;
title: string;
}>;
/**
* Callback invoked when an item in the dropdown is selected. The selected
* broadcast's boundStreamID will be passed back.
*/
onBroadcastSelected: Function;
/**
* The boundStreamID of the broadcast that should display as selected in the
* dropdown.
*/
selectedBoundStreamID: string;
}
/**
* A dropdown to select a YouTube broadcast.
*
* @augments Component
*/
class StreamKeyPicker extends PureComponent<Props> {
/**
* Default values for {@code StreamKeyForm} component's properties.
*
* @static
*/
static defaultProps = {
broadcasts: []
};
/**
* The initial state of a {@code StreamKeyForm} instance.
*/
state = {
isDropdownOpen: false
};
/**
* Initializes a new {@code StreamKeyPicker} instance.
*
* @param {Props} props - The React {@code Component} props to initialize
* the new {@code StreamKeyPicker} instance with.
*/
constructor(props: Props) {
super(props);
// Bind event handlers so they are only bound once per instance.
this._onSelect = this._onSelect.bind(this);
}
/**
* Implements React's {@link Component#render()}.
*
* @inheritdoc
* @returns {ReactElement}
*/
render() {
const { broadcasts, selectedBoundStreamID, t } = this.props;
if (!broadcasts.length) {
return (
<a
className = 'warning-text'
href = { YOUTUBE_LIVE_DASHBOARD_URL }
rel = 'noopener noreferrer'
target = '_blank'>
{ t('liveStreaming.getStreamKeyManually') }
</a>
);
}
const dropdownItems
= broadcasts.map(broadcast => {
return {
value: broadcast.boundStreamID,
label: broadcast.title
};
});
return (
<div className = 'broadcast-dropdown dropdown-menu'>
<Select
label = { t('liveStreaming.choose') }
onChange = { this._onSelect }
options = { dropdownItems }
value = { selectedBoundStreamID } />
</div>
);
}
/**
* Callback invoked when an item has been clicked in the dropdown menu.
*
* @param {Object} e - The key event to handle.
*
* @returns {void}
*/
_onSelect(e: React.ChangeEvent<HTMLSelectElement>) {
const streamId = e.target.value;
this.props.onBroadcastSelected(streamId);
}
}
export default translate(StreamKeyPicker);

View File

@ -10,7 +10,7 @@ import { translate } from '../../../base/i18n/functions';
import Checkbox from '../../../base/ui/components/web/Checkbox';
/**
* The type of the React {@code Component} props of {@link MoreTab}.
* The type of the React {@code Component} props of {@link ModeratorTab}.
*/
export type Props = AbstractDialogTabProps & WithTranslation & {
@ -60,7 +60,7 @@ export type Props = AbstractDialogTabProps & WithTranslation & {
*/
class ModeratorTab extends AbstractDialogTab<Props> {
/**
* Initializes a new {@code MoreTab} instance.
* Initializes a new {@code ModeratorTab} instance.
*
* @param {Object} props - The read-only properties with which the new
* instance is to be initialized.

View File

@ -1,23 +1,16 @@
/* eslint-disable lines-around-comment */
import DropdownMenu, {
DropdownItem,
DropdownItemGroup
} from '@atlaskit/dropdown-menu';
import React from 'react';
import { WithTranslation } from 'react-i18next';
// @ts-ignore
import keyboardShortcut from '../../../../../modules/keyboardshortcut/keyboardshortcut';
// @ts-ignore
import { AbstractDialogTab } from '../../../base/dialog';
// @ts-ignore
import type { Props as AbstractDialogTabProps } from '../../../base/dialog';
import AbstractDialogTab, {
Props as AbstractDialogTabProps
} from '../../../base/dialog/components/web/AbstractDialogTab';
import { translate } from '../../../base/i18n/functions';
import Checkbox from '../../../base/ui/components/web/Checkbox';
// @ts-ignore
import TouchmoveHack from '../../../chat/components/web/TouchmoveHack';
import Select from '../../../base/ui/components/web/Select';
import { MAX_ACTIVE_PARTICIPANTS } from '../../../filmstrip/constants';
// @ts-ignore
import { SS_DEFAULT_FRAME_RATE } from '../../constants';
/**
@ -66,6 +59,11 @@ export type Props = AbstractDialogTabProps & WithTranslation & {
*/
languages: Array<string>;
/**
* The number of max participants to display on stage.
*/
maxStageParticipants: number;
/**
* Whether or not to display the language select dropdown.
*/
@ -91,34 +89,23 @@ export type Props = AbstractDialogTabProps & WithTranslation & {
*/
showPrejoinSettings: boolean;
/**
* Wether or not the stage filmstrip is enabled.
*/
stageFilmstripEnabled: boolean;
/**
* Invoked to obtain translated strings.
*/
t: Function;
};
/**
* The type of the React {@code Component} state of {@link MoreTab}.
*/
type State = {
/**
* Whether or not the desktop share frame rate select dropdown is open.
*/
isFramerateSelectOpen: boolean;
/**
* Whether or not the language select dropdown is open.
*/
isLanguageSelectOpen: boolean;
};
/**
* React {@code Component} for modifying language and moderator settings.
*
* @augments Component
*/
class MoreTab extends AbstractDialogTab<Props, State> {
class MoreTab extends AbstractDialogTab<Props, {}> {
/**
* Initializes a new {@code MoreTab} instance.
*
@ -128,17 +115,8 @@ class MoreTab extends AbstractDialogTab<Props, State> {
constructor(props: Props) {
super(props);
// @ts-ignore
this.state = {
isFramerateSelectOpen: false,
isLanguageSelectOpen: false,
isMaxStageParticipantsOpen: false
};
// Bind event handler so it is only bound once for every instance.
this._onFramerateDropdownOpenChange = this._onFramerateDropdownOpenChange.bind(this);
this._onFramerateItemSelect = this._onFramerateItemSelect.bind(this);
this._onLanguageDropdownOpenChange = this._onLanguageDropdownOpenChange.bind(this);
this._onLanguageItemSelect = this._onLanguageItemSelect.bind(this);
this._onEnabledNotificationsChanged = this._onEnabledNotificationsChanged.bind(this);
this._onShowPrejoinPageChanged = this._onShowPrejoinPageChanged.bind(this);
@ -146,7 +124,6 @@ class MoreTab extends AbstractDialogTab<Props, State> {
this._onHideSelfViewChanged = this._onHideSelfViewChanged.bind(this);
this._renderMaxStageParticipantsSelect = this._renderMaxStageParticipantsSelect.bind(this);
this._onMaxStageParticipantsSelect = this._onMaxStageParticipantsSelect.bind(this);
this._onMaxStageParticipantsOpenChange = this._onMaxStageParticipantsOpenChange.bind(this);
}
/**
@ -170,18 +147,6 @@ class MoreTab extends AbstractDialogTab<Props, State> {
);
}
/**
* Callback invoked to toggle display of the desktop share framerate select dropdown.
*
* @param {Object} event - The event for opening or closing the dropdown.
* @private
* @returns {void}
*/
_onFramerateDropdownOpenChange({ isOpen }: { isOpen: boolean; }) {
// @ts-ignore
this.setState({ isFramerateSelectOpen: isOpen });
}
/**
* Callback invoked to select a frame rate from the select dropdown.
*
@ -190,23 +155,11 @@ class MoreTab extends AbstractDialogTab<Props, State> {
* @returns {void}
*/
_onFramerateItemSelect(e: React.ChangeEvent<HTMLSelectElement>) {
const frameRate = e.currentTarget.getAttribute('data-framerate');
const frameRate = e.target.value;
super._onChange({ currentFramerate: frameRate });
}
/**
* Callback invoked to toggle display of the language select dropdown.
*
* @param {Object} event - The event for opening or closing the dropdown.
* @private
* @returns {void}
*/
_onLanguageDropdownOpenChange({ isOpen }: { isOpen: boolean; }) {
// @ts-ignore
this.setState({ isLanguageSelectOpen: isOpen });
}
/**
* Callback invoked to select a language from select dropdown.
*
@ -215,7 +168,7 @@ class MoreTab extends AbstractDialogTab<Props, State> {
* @returns {void}
*/
_onLanguageItemSelect(e: React.ChangeEvent<HTMLSelectElement>) {
const language = e.currentTarget.getAttribute('data-language');
const language = e.target.value;
super._onChange({ currentLanguage: language });
}
@ -244,7 +197,6 @@ class MoreTab extends AbstractDialogTab<Props, State> {
_onEnabledNotificationsChanged({ target: { checked } }: React.ChangeEvent<HTMLInputElement>, type: any) {
super._onChange({
enabledNotifications: {
// @ts-ignore
...this.props.enabledNotifications,
[type]: checked
}
@ -275,18 +227,6 @@ class MoreTab extends AbstractDialogTab<Props, State> {
super._onChange({ hideSelfView: checked });
}
/**
* Callback invoked to toggle display of the max stage participants select dropdown.
*
* @param {Object} event - The event for opening or closing the dropdown.
* @private
* @returns {void}
*/
_onMaxStageParticipantsOpenChange({ isOpen }: { isOpen: boolean; }) {
// @ts-ignore
this.setState({ isMaxStageParticipantsOpen: isOpen });
}
/**
* Callback invoked to select a max number of stage participants from the select dropdown.
*
@ -306,50 +246,27 @@ class MoreTab extends AbstractDialogTab<Props, State> {
* @returns {ReactElement}
*/
_renderFramerateSelect() {
// @ts-ignore
const { currentFramerate, desktopShareFramerates, t } = this.props;
const frameRateItems = desktopShareFramerates.map((frameRate: string) => (
<DropdownItem
data-framerate = { frameRate }
key = { frameRate }
onClick = { this._onFramerateItemSelect }>
{ `${frameRate} ${t('settings.framesPerSecond')}` }
</DropdownItem>));
const frameRateItems = desktopShareFramerates.map((frameRate: number) => {
return {
value: frameRate,
label: `${frameRate} ${t('settings.framesPerSecond')}`
};
});
return (
<div
className = 'settings-sub-pane-element'
key = 'frameRate'>
<h2 className = 'mock-atlaskit-label'>
{ t('settings.desktopShareFramerate') }
</h2>
<div className = 'dropdown-menu'>
<TouchmoveHack
flex = { true }
isModal = { true }>
<DropdownMenu
// @ts-ignore
isOpen = { this.state.isFramerateSelectOpen }
onOpenChange = { this._onFramerateDropdownOpenChange }
shouldFitContainer = { true }
trigger = { currentFramerate
? `${currentFramerate} ${t('settings.framesPerSecond')}`
: '' }
triggerButtonProps = {{
shouldFitContainer: true
}}
triggerType = 'button'>
<DropdownItemGroup>
{ frameRateItems }
</DropdownItemGroup>
</DropdownMenu>
</TouchmoveHack>
</div>
<div
className = 'mock-atlaskit-label'>
{ parseInt(currentFramerate, 10) > SS_DEFAULT_FRAME_RATE
? t('settings.desktopShareHighFpsWarning')
: t('settings.desktopShareWarning') }
<Select
bottomLabel = { parseInt(currentFramerate, 10) > SS_DEFAULT_FRAME_RATE
? t('settings.desktopShareHighFpsWarning')
: t('settings.desktopShareWarning') }
label = { t('settings.desktopShareFramerate') }
onChange = { this._onFramerateItemSelect }
options = { frameRateItems }
value = { currentFramerate } />
</div>
</div>
);
@ -362,16 +279,15 @@ class MoreTab extends AbstractDialogTab<Props, State> {
* @returns {ReactElement}
*/
_renderKeyboardShortcutCheckbox() {
// @ts-ignore
const { t } = this.props;
return (
<div
className = 'settings-sub-pane-element'
key = 'keyboard-shortcut'>
<h2 className = 'mock-atlaskit-label'>
<span className = 'checkbox-label'>
{ t('keyboardShortcuts.keyboardShortcuts') }
</h2>
</span>
<Checkbox
checked = { keyboardShortcut.getEnabled() }
label = { t('prejoin.keyboardShortcuts') }
@ -388,16 +304,15 @@ class MoreTab extends AbstractDialogTab<Props, State> {
* @returns {ReactElement}
*/
_renderSelfViewCheckbox() {
// @ts-ignore
const { hideSelfView, t } = this.props;
return (
<div
className = 'settings-sub-pane-element'
key = 'selfview'>
<h2 className = 'mock-atlaskit-label'>
<span className = 'checkbox-label'>
{ t('settings.selfView') }
</h2>
</span>
<Checkbox
checked = { hideSelfView }
label = { t('videothumbnail.hideSelfView') }
@ -418,46 +333,26 @@ class MoreTab extends AbstractDialogTab<Props, State> {
currentLanguage,
languages,
t
// @ts-ignore
} = this.props;
const languageItems
= languages.map((language: string) => (
<DropdownItem
data-language = { language }
key = { language }
onClick = { this._onLanguageItemSelect }>
{ t(`languages:${language}`) }
</DropdownItem>));
= languages.map((language: string) => {
return {
value: language,
label: t(`languages:${language}`)
};
});
return (
<div
className = 'settings-sub-pane-element'
key = 'language'>
<h2 className = 'mock-atlaskit-label'>
{ t('settings.language') }
</h2>
<div className = 'dropdown-menu'>
<TouchmoveHack
flex = { true }
isModal = { true }>
<DropdownMenu
// @ts-ignore
isOpen = { this.state.isLanguageSelectOpen }
onOpenChange = { this._onLanguageDropdownOpenChange }
shouldFitContainer = { true }
trigger = { currentLanguage
? t(`languages:${currentLanguage}`)
: '' }
triggerButtonProps = {{
shouldFitContainer: true
}}
triggerType = 'button'>
<DropdownItemGroup>
{ languageItems }
</DropdownItemGroup>
</DropdownMenu>
</TouchmoveHack>
<Select
label = { t('settings.language') }
onChange = { this._onLanguageItemSelect }
options = { languageItems }
value = { currentLanguage } />
</div>
</div>
);
@ -470,16 +365,15 @@ class MoreTab extends AbstractDialogTab<Props, State> {
* @returns {ReactElement}
*/
_renderPrejoinScreenSettings() {
// @ts-ignore
const { t, showPrejoinPage } = this.props;
return (
<div
className = 'settings-sub-pane-element'
key = 'prejoin-screen'>
<h2 className = 'mock-atlaskit-label'>
<span className = 'checkbox-label'>
{ t('prejoin.premeeting') }
</h2>
</span>
<Checkbox
checked = { showPrejoinPage }
label = { t('prejoin.showScreen') }
@ -496,20 +390,19 @@ class MoreTab extends AbstractDialogTab<Props, State> {
* @returns {ReactElement}
*/
_renderNotificationsSettings() {
// @ts-ignore
const { t, enabledNotifications } = this.props;
return (
<div
className = 'settings-sub-pane-element'
key = 'notifications'>
<h2 className = 'mock-atlaskit-label'>
<span className = 'checkbox-label'>
{ t('notify.displayNotifications') }
</h2>
</span>
{
Object.keys(enabledNotifications).map(key => (
<Checkbox
checked = { enabledNotifications[key] }
checked = { Boolean(enabledNotifications[key as keyof typeof enabledNotifications]) }
key = { key }
label = { t(key) }
name = { `show-${key}` }
@ -527,47 +420,29 @@ class MoreTab extends AbstractDialogTab<Props, State> {
* @returns {ReactElement}
*/
_renderMaxStageParticipantsSelect() {
// @ts-ignore
const { maxStageParticipants, t, stageFilmstripEnabled } = this.props;
if (!stageFilmstripEnabled) {
return null;
}
const maxParticipantsItems = Array(MAX_ACTIVE_PARTICIPANTS).fill(0)
.map((no, index) => (
<DropdownItem
data-maxparticipants = { index + 1 }
key = { index + 1 }
onClick = { this._onMaxStageParticipantsSelect }>
{index + 1}
</DropdownItem>));
.map((no, index) => {
return {
value: index + 1,
label: `${index + 1}`
};
});
return (
<div
className = 'settings-sub-pane-element'
key = 'maxStageParticipants'>
<h2 className = 'mock-atlaskit-label'>
{ t('settings.maxStageParticipants') }
</h2>
<div className = 'dropdown-menu'>
<TouchmoveHack
flex = { true }
isModal = { true }>
<DropdownMenu
// @ts-ignore
isOpen = { this.state.isMaxStageParticipantsOpen }
onOpenChange = { this._onMaxStageParticipantsOpenChange }
shouldFitContainer = { true }
trigger = { maxStageParticipants }
triggerButtonProps = {{
shouldFitContainer: true
}}
triggerType = 'button'>
<DropdownItemGroup>
{ maxParticipantsItems }
</DropdownItemGroup>
</DropdownMenu>
</TouchmoveHack>
<Select
label = { t('settings.maxStageParticipants') }
onChange = { this._onMaxStageParticipantsSelect }
options = { maxParticipantsItems }
value = { maxStageParticipants } />
</div>
</div>
);
@ -580,7 +455,6 @@ class MoreTab extends AbstractDialogTab<Props, State> {
* @returns {ReactElement}
*/
_renderSettingsRight() {
// @ts-ignore
const { showLanguageSettings } = this.props;
return (
@ -600,7 +474,6 @@ class MoreTab extends AbstractDialogTab<Props, State> {
* @returns {ReactElement}
*/
_renderSettingsLeft() {
// @ts-ignore
const { disableHideSelfView, showNotificationsSettings, showPrejoinSettings } = this.props;
return (
@ -616,5 +489,4 @@ class MoreTab extends AbstractDialogTab<Props, State> {
}
}
// @ts-ignore
export default translate(MoreTab);

View File

@ -9,6 +9,7 @@ import { getAvailableDevices } from '../../../base/devices';
// @ts-ignore
import { DialogWithTabs, hideDialog } from '../../../base/dialog';
import { connect } from '../../../base/redux/functions';
import { withPixelLineHeight } from '../../../base/styles/functions.web';
// @ts-ignore
import { isCalendarEnabled } from '../../../calendar-sync';
import {
@ -113,6 +114,14 @@ const styles = (theme: Theme) => {
padding: `20px 0px ${theme.spacing(1)} 0px`
},
'& .checkbox-label': {
color: theme.palette.text01,
...withPixelLineHeight(theme.typography.bodyShortRegular),
marginBottom: theme.spacing(2),
display: 'block',
marginTop: '20px'
},
'& input[type="checkbox"]:checked + svg': {
'--checkbox-background-color': '#6492e7',
'--checkbox-border-color': '#6492e7'
@ -158,6 +167,10 @@ const styles = (theme: Theme) => {
flex: 1
},
'& .dropdown-menu': {
marginTop: '20px'
},
'& .settings-checkbox': {
display: 'flex',
marginBottom: theme.spacing(2)