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 7.0KB

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