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

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