You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

DeviceSelector.js 6.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. import AKButton from '@atlaskit/button';
  2. import AKDropdownMenu from '@atlaskit/dropdown-menu';
  3. import ExpandIcon from '@atlaskit/icon/glyph/expand';
  4. import React, { Component } from 'react';
  5. import { translate } from '../../base/i18n';
  6. const EXPAND_ICON = <ExpandIcon label = 'expand' />;
  7. /**
  8. * React component for selecting a device from a select element. Wraps
  9. * AKDropdownMenu with device selection specific logic.
  10. *
  11. * @extends Component
  12. */
  13. class DeviceSelector extends Component {
  14. /**
  15. * DeviceSelector component's property types.
  16. *
  17. * @static
  18. */
  19. static propTypes = {
  20. /**
  21. * MediaDeviceInfos used for display in the select element.
  22. */
  23. devices: React.PropTypes.array,
  24. /**
  25. * If false, will return a selector with no selection options.
  26. */
  27. hasPermission: React.PropTypes.bool,
  28. /**
  29. * CSS class for the icon to the left of the dropdown trigger.
  30. */
  31. icon: React.PropTypes.string,
  32. /**
  33. * If true, will render the selector disabled with a default selection.
  34. */
  35. isDisabled: React.PropTypes.bool,
  36. /**
  37. * The translation key to display as a menu label.
  38. */
  39. label: React.PropTypes.string,
  40. /**
  41. * The callback to invoke when a selection is made.
  42. */
  43. onSelect: React.PropTypes.func,
  44. /**
  45. * The default device to display as selected.
  46. */
  47. selectedDeviceId: React.PropTypes.string,
  48. /**
  49. * Invoked to obtain translated strings.
  50. */
  51. t: React.PropTypes.func
  52. }
  53. /**
  54. * Initializes a new DeviceSelector instance.
  55. *
  56. * @param {Object} props - The read-only React Component props with which
  57. * the new instance is to be initialized.
  58. */
  59. constructor(props) {
  60. super(props);
  61. this._onSelect = this._onSelect.bind(this);
  62. }
  63. /**
  64. * Implements React's {@link Component#render()}.
  65. *
  66. * @inheritdoc
  67. * @returns {ReactElement}
  68. */
  69. render() {
  70. if (!this.props.hasPermission) {
  71. return this._renderNoPermission();
  72. }
  73. if (!this.props.devices.length) {
  74. return this._renderNoDevices();
  75. }
  76. const items = this.props.devices.map(this._createDropdownItem);
  77. const defaultSelected = items.find(item =>
  78. item.value === this.props.selectedDeviceId
  79. );
  80. return this._createDropdown({
  81. defaultSelected,
  82. isDisabled: this.props.isDisabled,
  83. items,
  84. placeholder: 'deviceSelection.selectADevice'
  85. });
  86. }
  87. /**
  88. * Creates an AtlasKit Button.
  89. *
  90. * @param {string} buttonText - The text to display within the button.
  91. * @private
  92. * @returns {ReactElement}
  93. */
  94. _createDropdownTrigger(buttonText) {
  95. return (
  96. <AKButton
  97. className = 'device-selector-trigger'
  98. iconAfter = { EXPAND_ICON }
  99. iconBefore = { this._createDropdownIcon() }>
  100. { buttonText }
  101. </AKButton>
  102. );
  103. }
  104. /**
  105. * Creates a ReactComponent for displaying an icon.
  106. *
  107. * @private
  108. * @returns {ReactElement}
  109. */
  110. _createDropdownIcon() {
  111. return (
  112. <span className = { `device-selector-icon ${this.props.icon}` } />
  113. );
  114. }
  115. /**
  116. * Creates an object in the format expected by AKDropdownMenu for an option.
  117. *
  118. * @param {MediaDeviceInfo} device - An object with a label and a deviceId.
  119. * @private
  120. * @returns {Object} The passed in media device description converted to a
  121. * format recognized as a valid AKDropdownMenu item.
  122. */
  123. _createDropdownItem(device) {
  124. return {
  125. content: device.label,
  126. value: device.deviceId
  127. };
  128. }
  129. /**
  130. * Creates a AKDropdownMenu Component using passed in props and options.
  131. *
  132. * @param {Object} options - Additional configuration for display.
  133. * @param {Object} options.defaultSelected - The option that should be set
  134. * as currently chosen.
  135. * @param {boolean} options.isDisabled - If true, AKDropdownMenu will not
  136. * open on click.
  137. * @param {Array} options.items - All the selectable options to display.
  138. * @param {string} options.placeholder - The translation key to display when
  139. * no selection has been made.
  140. * @private
  141. * @returns {ReactElement}
  142. */
  143. _createDropdown(options) {
  144. const triggerText
  145. = (options.defaultSelected && options.defaultSelected.content)
  146. || options.placeholder;
  147. return (
  148. <AKDropdownMenu
  149. { ...(options.isDisabled && { isOpen: !options.isDisabled }) }
  150. items = { [ { items: options.items || [] } ] }
  151. noMatchesFound
  152. = { this.props.t('deviceSelection.noOtherDevices') }
  153. onItemActivated = { this._onSelect }>
  154. { this._createDropdownTrigger(triggerText) }
  155. </AKDropdownMenu>
  156. );
  157. }
  158. /**
  159. * Invokes the passed in callback to notify of selection changes.
  160. *
  161. * @param {Object} selection - Event from choosing a AKDropdownMenu option.
  162. * @private
  163. * @returns {void}
  164. */
  165. _onSelect(selection) {
  166. const newDeviceId = selection.item.value;
  167. if (this.props.selectedDeviceId !== newDeviceId) {
  168. this.props.onSelect(selection.item.value);
  169. }
  170. }
  171. /**
  172. * Creates a Select Component that is disabled and has a placeholder
  173. * indicating there are no devices to select.
  174. *
  175. * @private
  176. * @returns {ReactElement}
  177. */
  178. _renderNoDevices() {
  179. return this._createDropdown({
  180. isDisabled: true,
  181. placeholder: this.props.t('settings.noDevice')
  182. });
  183. }
  184. /**
  185. * Creates a AKDropdownMenu Component that is disabled and has a placeholder
  186. * stating there is no permission to display the devices.
  187. *
  188. * @private
  189. * @returns {ReactElement}
  190. */
  191. _renderNoPermission() {
  192. return this._createDropdown({
  193. isDisabled: true,
  194. placeholder: this.props.t('settings.noPermission')
  195. });
  196. }
  197. }
  198. export default translate(DeviceSelector);