Quellcode durchsuchen

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.
j8
Leonard Kim vor 8 Jahren
Ursprung
Commit
3e518e8040

+ 35
- 14
css/modals/device-selection/_device-selection.scss Datei anzeigen

@@ -4,37 +4,58 @@
4 4
     .device-selectors {
5 5
         font-size: 14px;
6 6
 
7
+        /* ensure all child components do not exceed parent width */
8
+        button,
9
+        div {
10
+            max-width: 100%;
11
+        }
12
+
7 13
         > div {
14
+            display: block;
8 15
             margin-bottom: 10px;
9 16
         }
10 17
 
11 18
         > div:last-child {
12 19
             margin-bottom: 5px;
13 20
         }
21
+
22
+        .device-selector-icon {
23
+            color: inherit;
24
+            font-size: 20px;
25
+        }
14 26
     }
15 27
 
16
-    .device-selection-column-selectors,
17
-    .device-selection-column-video {
18
-        padding: 10px;
28
+    .device-selection-column {
29
+        box-sizing: border-box;
19 30
         display: inline-block;
20 31
         vertical-align: top;
21
-    }
22
-    .device-selection-column-selectors {
23
-        width: 46%;
24
-    }
25
-    .device-selection-column-video {
26
-        width: 49%;
27
-        padding: 10px 0;
32
+
33
+        &.column-selectors {
34
+            margin-left: 15px;
35
+            width: 45%;
36
+        }
37
+
38
+        &.column-video {
39
+            width: 50%;
40
+        }
28 41
     }
29 42
 
30 43
     .device-selection-video-container {
44
+        /* TOFIX: to be removed when we move out from muted preview */
31 45
         background: black;
32
-        height: 156px;
33
-        margin: 15px 0 5px;
46
+        border-radius: 3px;
47
+        /* TOFIX-END */
48
+        height: 160px;
49
+        margin-bottom: 5px;
34 50
 
35 51
         .video-input-preview {
52
+            margin-top: 2px;
36 53
             position: relative;
37 54
 
55
+            > video {
56
+                border-radius: 3px;
57
+            }
58
+
38 59
             .video-input-preview-muted {
39 60
                 color: $participantNameColor;
40 61
                 display: none;
@@ -58,8 +79,8 @@
58 79
     }
59 80
 
60 81
     .audio-output-preview {
61
-        text-align: right;
62
-
82
+        font-size: 14px;
83
+        margin-top: 10px;
63 84
         a {
64 85
             cursor: pointer;
65 86
             text-decoration: none;

+ 2
- 1
package.json Datei anzeigen

@@ -18,9 +18,10 @@
18 18
   "dependencies": {
19 19
     "@atlaskit/button": "1.0.3",
20 20
     "@atlaskit/button-group": "1.0.0",
21
+    "@atlaskit/dropdown-menu": "1.1.12",
21 22
     "@atlaskit/field-text": "2.0.3",
23
+    "@atlaskit/icon": "6.0.0",
22 24
     "@atlaskit/modal-dialog": "1.2.4",
23
-    "@atlaskit/single-select": "1.6.1",
24 25
     "@atlaskit/tabs": "1.2.5",
25 26
     "@atlassian/aui": "6.0.6",
26 27
     "async": "0.9.0",

+ 10
- 7
react/features/device-selection/components/DeviceSelectionDialog.js Datei anzeigen

@@ -170,13 +170,7 @@ class DeviceSelectionDialog extends Component {
170 170
                 onSubmit = { this._onSubmit }
171 171
                 titleKey = 'deviceSelection.deviceSettings' >
172 172
                 <div className = 'device-selection'>
173
-                    <div className = 'device-selection-column-selectors'>
174
-                        <div className = 'device-selectors'>
175
-                            { this._renderSelectors() }
176
-                        </div>
177
-                        { this._renderAudioOutputPreview() }
178
-                    </div>
179
-                    <div className = 'device-selection-column-video'>
173
+                    <div className = 'device-selection-column column-video'>
180 174
                         <div className = 'device-selection-video-container'>
181 175
                             <VideoInputPreview
182 176
                                 track = { this.state.previewVideoTrack
@@ -184,6 +178,12 @@ class DeviceSelectionDialog extends Component {
184 178
                         </div>
185 179
                         { this._renderAudioInputPreview() }
186 180
                     </div>
181
+                    <div className = 'device-selection-column column-selectors'>
182
+                        <div className = 'device-selectors'>
183
+                            { this._renderSelectors() }
184
+                        </div>
185
+                        { this._renderAudioOutputPreview() }
186
+                    </div>
187 187
                 </div>
188 188
             </Dialog>
189 189
         );
@@ -543,6 +543,7 @@ class DeviceSelectionDialog extends Component {
543 543
             {
544 544
                 devices: availableDevices.videoInput,
545 545
                 hasPermission: this.props.hasVideoPermission,
546
+                icon: 'icon-camera',
546 547
                 isDisabled: this.props.disableDeviceChange,
547 548
                 key: 'videoInput',
548 549
                 label: 'settings.selectCamera',
@@ -552,6 +553,7 @@ class DeviceSelectionDialog extends Component {
552 553
             {
553 554
                 devices: availableDevices.audioInput,
554 555
                 hasPermission: this.props.hasAudioPermission,
556
+                icon: 'icon-microphone',
555 557
                 isDisabled: this.props.disableAudioInputChange
556 558
                     || this.props.disableDeviceChange,
557 559
                 key: 'audioInput',
@@ -566,6 +568,7 @@ class DeviceSelectionDialog extends Component {
566 568
                 devices: availableDevices.audioOutput,
567 569
                 hasPermission: this.props.hasAudioPermission
568 570
                     || this.props.hasVideoPermission,
571
+                icon: 'icon-volume',
569 572
                 isDisabled: this.props.disableDeviceChange,
570 573
                 key: 'audioOutput',
571 574
                 label: 'settings.selectAudioOutput',

+ 73
- 29
react/features/device-selection/components/DeviceSelector.js Datei anzeigen

@@ -1,11 +1,15 @@
1
-import Select from '@atlaskit/single-select';
1
+import AKButton from '@atlaskit/button';
2
+import AKDropdownMenu from '@atlaskit/dropdown-menu';
3
+import ExpandIcon from '@atlaskit/icon/glyph/expand';
2 4
 import React, { Component } from 'react';
3 5
 
4 6
 import { translate } from '../../base/i18n';
5 7
 
8
+const EXPAND_ICON = <ExpandIcon label = 'expand' />;
9
+
6 10
 /**
7
- * React component for selecting a device from a select element. Wraps Select
8
- * with device selection specific logic.
11
+ * React component for selecting a device from a select element. Wraps
12
+ * AKDropdownMenu with device selection specific logic.
9 13
  *
10 14
  * @extends Component
11 15
  */
@@ -26,6 +30,11 @@ class DeviceSelector extends Component {
26 30
          */
27 31
         hasPermission: React.PropTypes.bool,
28 32
 
33
+        /**
34
+         * CSS class for the icon to the left of the dropdown trigger.
35
+         */
36
+        icon: React.PropTypes.string,
37
+
29 38
         /**
30 39
          * If true, will render the selector disabled with a default selection.
31 40
          */
@@ -79,12 +88,12 @@ class DeviceSelector extends Component {
79 88
             return this._renderNoDevices();
80 89
         }
81 90
 
82
-        const items = this.props.devices.map(this._createSelectItem);
91
+        const items = this.props.devices.map(this._createDropdownItem);
83 92
         const defaultSelected = items.find(item =>
84 93
             item.value === this.props.selectedDeviceId
85 94
         );
86 95
 
87
-        return this._createSelector({
96
+        return this._createDropdown({
88 97
             defaultSelected,
89 98
             isDisabled: this.props.isDisabled,
90 99
             items,
@@ -93,14 +102,44 @@ class DeviceSelector extends Component {
93 102
     }
94 103
 
95 104
     /**
96
-     * Creates an object in the format expected by Select for an option element.
105
+     * Creates an AtlasKit Button.
106
+     *
107
+     * @param {string} buttonText - The text to display within the button.
108
+     * @private
109
+     * @returns {ReactElement}
110
+     */
111
+    _createDropdownTrigger(buttonText) {
112
+        return (
113
+            <AKButton
114
+                className = 'device-selector-trigger'
115
+                iconAfter = { EXPAND_ICON }
116
+                iconBefore = { this._createDropdownIcon() }>
117
+                { buttonText }
118
+            </AKButton>
119
+        );
120
+    }
121
+
122
+    /**
123
+     * Creates a ReactComponent for displaying an icon.
124
+     *
125
+     * @private
126
+     * @returns {ReactElement}
127
+     */
128
+    _createDropdownIcon() {
129
+        return (
130
+            <span className = { `device-selector-icon ${this.props.icon}` } />
131
+        );
132
+    }
133
+
134
+    /**
135
+     * Creates an object in the format expected by AKDropdownMenu for an option.
97 136
      *
98 137
      * @param {MediaDeviceInfo} device - An object with a label and a deviceId.
99 138
      * @private
100 139
      * @returns {Object} The passed in media device description converted to a
101
-     * format recognized as a valid Select item.
140
+     * format recognized as a valid AKDropdownMenu item.
102 141
      */
103
-    _createSelectItem(device) {
142
+    _createDropdownItem(device) {
104 143
         return {
105 144
             content: device.label,
106 145
             value: device.deviceId
@@ -108,44 +147,49 @@ class DeviceSelector extends Component {
108 147
     }
109 148
 
110 149
     /**
111
-     * Creates a Select Component using passed in props and options.
150
+     * Creates a AKDropdownMenu Component using passed in props and options.
112 151
      *
113
-     * @param {Object} options - Additional configuration for display Select.
152
+     * @param {Object} options - Additional configuration for display.
114 153
      * @param {Object} options.defaultSelected - The option that should be set
115 154
      * as currently chosen.
116
-     * @param {boolean} options.isDisabled - If true Select will not open on
117
-     * click.
155
+     * @param {boolean} options.isDisabled - If true, AKDropdownMenu will not
156
+     * open on click.
118 157
      * @param {Array} options.items - All the selectable options to display.
119 158
      * @param {string} options.placeholder - The translation key to display when
120 159
      * no selection has been made.
121 160
      * @private
122 161
      * @returns {ReactElement}
123 162
      */
124
-    _createSelector(options) {
163
+    _createDropdown(options) {
164
+        const triggerText
165
+            = (options.defaultSelected && options.defaultSelected.content)
166
+                || options.placeholder;
167
+
125 168
         return (
126
-            <Select
127
-                defaultSelected = { options.defaultSelected }
128
-                isDisabled = { options.isDisabled }
129
-                isFirstChild = { true }
169
+            <AKDropdownMenu
170
+                { ...(options.isDisabled && { isOpen: !options.isDisabled }) }
130 171
                 items = { [ { items: options.items || [] } ] }
131
-                label = { this.props.t(this.props.label) }
132 172
                 noMatchesFound
133 173
                     = { this.props.t('deviceSelection.noOtherDevices') }
134
-                onSelected = { this._onSelect }
135
-                placeholder = { this.props.t(options.placeholder) }
136
-                shouldFitContainer = { true } />
174
+                onItemActivated = { this._onSelect }>
175
+                { this._createDropdownTrigger(triggerText) }
176
+            </AKDropdownMenu>
137 177
         );
138 178
     }
139 179
 
140 180
     /**
141 181
      * Invokes the passed in callback to notify of selection changes.
142 182
      *
143
-     * @param {Object} selection - Event returned from Select.
183
+     * @param {Object} selection - Event from choosing a AKDropdownMenu option.
144 184
      * @private
145 185
      * @returns {void}
146 186
      */
147 187
     _onSelect(selection) {
148
-        this.props.onSelect(selection.item.value);
188
+        const newDeviceId = selection.item.value;
189
+
190
+        if (this.props.selectedDeviceId !== newDeviceId) {
191
+            this.props.onSelect(selection.item.value);
192
+        }
149 193
     }
150 194
 
151 195
     /**
@@ -156,23 +200,23 @@ class DeviceSelector extends Component {
156 200
      * @returns {ReactElement}
157 201
      */
158 202
     _renderNoDevices() {
159
-        return this._createSelector({
203
+        return this._createDropdown({
160 204
             isDisabled: true,
161
-            placeholder: 'settings.noDevice'
205
+            placeholder: this.props.t('settings.noDevice')
162 206
         });
163 207
     }
164 208
 
165 209
     /**
166
-     * Creates a Select Component that is disabled and has a placeholder stating
167
-     * there is no permission to display the devices.
210
+     * Creates a AKDropdownMenu Component that is disabled and has a placeholder
211
+     * stating there is no permission to display the devices.
168 212
      *
169 213
      * @private
170 214
      * @returns {ReactElement}
171 215
      */
172 216
     _renderNoPermission() {
173
-        return this._createSelector({
217
+        return this._createDropdown({
174 218
             isDisabled: true,
175
-            placeholder: 'settings.noPermission'
219
+            placeholder: this.props.t('settings.noPermission')
176 220
         });
177 221
     }
178 222
 }

Laden…
Abbrechen
Speichern