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.web.js 6.6KB

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