feat: convert device selection modal to use AtlasKit Dropdown
Instead of using AtlasKit Single-Select, use Dropdown. Dropdown differs in that an icon can be specified for the trigger element, whereas Single-Select currently supports icons for all elements, and Dropdown can show all options incuding the already-selected option. This change does introduce the issue of the trigger element not taking up 100% width of the parent. Supporting such would involve overriding AtlasKit CSS. The compromise made here was to do a generic override of max-width so the trigger elements at least stay within the parent and aligning the trigger elements to the right.
This commit is contained in:
parent
33c92a31bf
commit
3e518e8040
|
@ -4,37 +4,58 @@
|
|||
.device-selectors {
|
||||
font-size: 14px;
|
||||
|
||||
/* ensure all child components do not exceed parent width */
|
||||
button,
|
||||
div {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
> div {
|
||||
display: block;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
> div:last-child {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.device-selector-icon {
|
||||
color: inherit;
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.device-selection-column-selectors,
|
||||
.device-selection-column-video {
|
||||
padding: 10px;
|
||||
.device-selection-column {
|
||||
box-sizing: border-box;
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
}
|
||||
.device-selection-column-selectors {
|
||||
width: 46%;
|
||||
}
|
||||
.device-selection-column-video {
|
||||
width: 49%;
|
||||
padding: 10px 0;
|
||||
|
||||
&.column-selectors {
|
||||
margin-left: 15px;
|
||||
width: 45%;
|
||||
}
|
||||
|
||||
&.column-video {
|
||||
width: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
.device-selection-video-container {
|
||||
/* TOFIX: to be removed when we move out from muted preview */
|
||||
background: black;
|
||||
height: 156px;
|
||||
margin: 15px 0 5px;
|
||||
border-radius: 3px;
|
||||
/* TOFIX-END */
|
||||
height: 160px;
|
||||
margin-bottom: 5px;
|
||||
|
||||
.video-input-preview {
|
||||
margin-top: 2px;
|
||||
position: relative;
|
||||
|
||||
> video {
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.video-input-preview-muted {
|
||||
color: $participantNameColor;
|
||||
display: none;
|
||||
|
@ -58,8 +79,8 @@
|
|||
}
|
||||
|
||||
.audio-output-preview {
|
||||
text-align: right;
|
||||
|
||||
font-size: 14px;
|
||||
margin-top: 10px;
|
||||
a {
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
|
|
|
@ -18,9 +18,10 @@
|
|||
"dependencies": {
|
||||
"@atlaskit/button": "1.0.3",
|
||||
"@atlaskit/button-group": "1.0.0",
|
||||
"@atlaskit/dropdown-menu": "1.1.12",
|
||||
"@atlaskit/field-text": "2.0.3",
|
||||
"@atlaskit/icon": "6.0.0",
|
||||
"@atlaskit/modal-dialog": "1.2.4",
|
||||
"@atlaskit/single-select": "1.6.1",
|
||||
"@atlaskit/tabs": "1.2.5",
|
||||
"@atlassian/aui": "6.0.6",
|
||||
"async": "0.9.0",
|
||||
|
|
|
@ -170,13 +170,7 @@ class DeviceSelectionDialog extends Component {
|
|||
onSubmit = { this._onSubmit }
|
||||
titleKey = 'deviceSelection.deviceSettings' >
|
||||
<div className = 'device-selection'>
|
||||
<div className = 'device-selection-column-selectors'>
|
||||
<div className = 'device-selectors'>
|
||||
{ this._renderSelectors() }
|
||||
</div>
|
||||
{ this._renderAudioOutputPreview() }
|
||||
</div>
|
||||
<div className = 'device-selection-column-video'>
|
||||
<div className = 'device-selection-column column-video'>
|
||||
<div className = 'device-selection-video-container'>
|
||||
<VideoInputPreview
|
||||
track = { this.state.previewVideoTrack
|
||||
|
@ -184,6 +178,12 @@ class DeviceSelectionDialog extends Component {
|
|||
</div>
|
||||
{ this._renderAudioInputPreview() }
|
||||
</div>
|
||||
<div className = 'device-selection-column column-selectors'>
|
||||
<div className = 'device-selectors'>
|
||||
{ this._renderSelectors() }
|
||||
</div>
|
||||
{ this._renderAudioOutputPreview() }
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
);
|
||||
|
@ -543,6 +543,7 @@ class DeviceSelectionDialog extends Component {
|
|||
{
|
||||
devices: availableDevices.videoInput,
|
||||
hasPermission: this.props.hasVideoPermission,
|
||||
icon: 'icon-camera',
|
||||
isDisabled: this.props.disableDeviceChange,
|
||||
key: 'videoInput',
|
||||
label: 'settings.selectCamera',
|
||||
|
@ -552,6 +553,7 @@ class DeviceSelectionDialog extends Component {
|
|||
{
|
||||
devices: availableDevices.audioInput,
|
||||
hasPermission: this.props.hasAudioPermission,
|
||||
icon: 'icon-microphone',
|
||||
isDisabled: this.props.disableAudioInputChange
|
||||
|| this.props.disableDeviceChange,
|
||||
key: 'audioInput',
|
||||
|
@ -566,6 +568,7 @@ class DeviceSelectionDialog extends Component {
|
|||
devices: availableDevices.audioOutput,
|
||||
hasPermission: this.props.hasAudioPermission
|
||||
|| this.props.hasVideoPermission,
|
||||
icon: 'icon-volume',
|
||||
isDisabled: this.props.disableDeviceChange,
|
||||
key: 'audioOutput',
|
||||
label: 'settings.selectAudioOutput',
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
import Select from '@atlaskit/single-select';
|
||||
import AKButton from '@atlaskit/button';
|
||||
import AKDropdownMenu from '@atlaskit/dropdown-menu';
|
||||
import ExpandIcon from '@atlaskit/icon/glyph/expand';
|
||||
import React, { Component } from 'react';
|
||||
|
||||
import { translate } from '../../base/i18n';
|
||||
|
||||
const EXPAND_ICON = <ExpandIcon label = 'expand' />;
|
||||
|
||||
/**
|
||||
* React component for selecting a device from a select element. Wraps Select
|
||||
* with device selection specific logic.
|
||||
* React component for selecting a device from a select element. Wraps
|
||||
* AKDropdownMenu with device selection specific logic.
|
||||
*
|
||||
* @extends Component
|
||||
*/
|
||||
|
@ -26,6 +30,11 @@ class DeviceSelector extends Component {
|
|||
*/
|
||||
hasPermission: React.PropTypes.bool,
|
||||
|
||||
/**
|
||||
* CSS class for the icon to the left of the dropdown trigger.
|
||||
*/
|
||||
icon: React.PropTypes.string,
|
||||
|
||||
/**
|
||||
* If true, will render the selector disabled with a default selection.
|
||||
*/
|
||||
|
@ -79,12 +88,12 @@ class DeviceSelector extends Component {
|
|||
return this._renderNoDevices();
|
||||
}
|
||||
|
||||
const items = this.props.devices.map(this._createSelectItem);
|
||||
const items = this.props.devices.map(this._createDropdownItem);
|
||||
const defaultSelected = items.find(item =>
|
||||
item.value === this.props.selectedDeviceId
|
||||
);
|
||||
|
||||
return this._createSelector({
|
||||
return this._createDropdown({
|
||||
defaultSelected,
|
||||
isDisabled: this.props.isDisabled,
|
||||
items,
|
||||
|
@ -93,14 +102,44 @@ class DeviceSelector extends Component {
|
|||
}
|
||||
|
||||
/**
|
||||
* Creates an object in the format expected by Select for an option element.
|
||||
* Creates an AtlasKit Button.
|
||||
*
|
||||
* @param {string} buttonText - The text to display within the button.
|
||||
* @private
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
_createDropdownTrigger(buttonText) {
|
||||
return (
|
||||
<AKButton
|
||||
className = 'device-selector-trigger'
|
||||
iconAfter = { EXPAND_ICON }
|
||||
iconBefore = { this._createDropdownIcon() }>
|
||||
{ buttonText }
|
||||
</AKButton>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a ReactComponent for displaying an icon.
|
||||
*
|
||||
* @private
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
_createDropdownIcon() {
|
||||
return (
|
||||
<span className = { `device-selector-icon ${this.props.icon}` } />
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 Select item.
|
||||
* format recognized as a valid AKDropdownMenu item.
|
||||
*/
|
||||
_createSelectItem(device) {
|
||||
_createDropdownItem(device) {
|
||||
return {
|
||||
content: device.label,
|
||||
value: device.deviceId
|
||||
|
@ -108,44 +147,49 @@ class DeviceSelector extends Component {
|
|||
}
|
||||
|
||||
/**
|
||||
* Creates a Select Component using passed in props and options.
|
||||
* Creates a AKDropdownMenu Component using passed in props and options.
|
||||
*
|
||||
* @param {Object} options - Additional configuration for display Select.
|
||||
* @param {Object} options - Additional configuration for display.
|
||||
* @param {Object} options.defaultSelected - The option that should be set
|
||||
* as currently chosen.
|
||||
* @param {boolean} options.isDisabled - If true Select will not open on
|
||||
* click.
|
||||
* @param {boolean} options.isDisabled - If true, AKDropdownMenu will not
|
||||
* open on click.
|
||||
* @param {Array} options.items - All the selectable options to display.
|
||||
* @param {string} options.placeholder - The translation key to display when
|
||||
* no selection has been made.
|
||||
* @private
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
_createSelector(options) {
|
||||
_createDropdown(options) {
|
||||
const triggerText
|
||||
= (options.defaultSelected && options.defaultSelected.content)
|
||||
|| options.placeholder;
|
||||
|
||||
return (
|
||||
<Select
|
||||
defaultSelected = { options.defaultSelected }
|
||||
isDisabled = { options.isDisabled }
|
||||
isFirstChild = { true }
|
||||
<AKDropdownMenu
|
||||
{ ...(options.isDisabled && { isOpen: !options.isDisabled }) }
|
||||
items = { [ { items: options.items || [] } ] }
|
||||
label = { this.props.t(this.props.label) }
|
||||
noMatchesFound
|
||||
= { this.props.t('deviceSelection.noOtherDevices') }
|
||||
onSelected = { this._onSelect }
|
||||
placeholder = { this.props.t(options.placeholder) }
|
||||
shouldFitContainer = { true } />
|
||||
onItemActivated = { this._onSelect }>
|
||||
{ this._createDropdownTrigger(triggerText) }
|
||||
</AKDropdownMenu>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes the passed in callback to notify of selection changes.
|
||||
*
|
||||
* @param {Object} selection - Event returned from Select.
|
||||
* @param {Object} selection - Event from choosing a AKDropdownMenu option.
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
_onSelect(selection) {
|
||||
this.props.onSelect(selection.item.value);
|
||||
const newDeviceId = selection.item.value;
|
||||
|
||||
if (this.props.selectedDeviceId !== newDeviceId) {
|
||||
this.props.onSelect(selection.item.value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -156,23 +200,23 @@ class DeviceSelector extends Component {
|
|||
* @returns {ReactElement}
|
||||
*/
|
||||
_renderNoDevices() {
|
||||
return this._createSelector({
|
||||
return this._createDropdown({
|
||||
isDisabled: true,
|
||||
placeholder: 'settings.noDevice'
|
||||
placeholder: this.props.t('settings.noDevice')
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a Select Component that is disabled and has a placeholder stating
|
||||
* there is no permission to display the devices.
|
||||
* Creates a AKDropdownMenu Component that is disabled and has a placeholder
|
||||
* stating there is no permission to display the devices.
|
||||
*
|
||||
* @private
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
_renderNoPermission() {
|
||||
return this._createSelector({
|
||||
return this._createDropdown({
|
||||
isDisabled: true,
|
||||
placeholder: 'settings.noPermission'
|
||||
placeholder: this.props.t('settings.noPermission')
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue