Parcourir la source

feat(popover): create a wrapper around InlineDialog

master
Leonard Kim il y a 7 ans
Parent
révision
85f0ad2791

+ 33
- 0
css/_popover.scss Voir le fichier

@@ -0,0 +1,33 @@
1
+/**
2
+ * Mousemove padding styles are used to add invisible elements to the popover
3
+ * to allow mouse movement from the popover trigger to the popover itself
4
+ * without triggering a mouseleave event.
5
+ */
6
+.popover-mousemove-padding-bottom {
7
+    bottom: -15px;
8
+    height: 20px;
9
+    position: absolute;
10
+    right: 0;
11
+    width: 100%;
12
+}
13
+.popover-mousemove-padding-right {
14
+    height: 100%;
15
+    position: absolute;
16
+    right: -20;
17
+    top: 0;
18
+    width: 40px;
19
+}
20
+
21
+/**
22
+ * An invisible element is added to the top of the popover to ensure the mouse
23
+ * stays over the popover when the popover's height is shrunk, which would then
24
+ * normally leave the mouse outside of the popover itself and cause a mouseleave
25
+ * event.
26
+ */
27
+.popover-mouse-padding-top {
28
+    height: 30px;
29
+    position: absolute;
30
+    right: 0;
31
+    top: -25px;
32
+    width: 100%;
33
+}

+ 0
- 22
css/_videolayout_default.scss Voir le fichier

@@ -378,28 +378,6 @@
378 378
 .remote-video-menu-trigger {
379 379
     margin-top: 7px;
380 380
 }
381
-.popover-mousemove-padding-bottom {
382
-    bottom: -15px;
383
-    height: 20px;
384
-    position: absolute;
385
-    right: 0;
386
-    width: 100%;
387
-}
388
-.popover-mousemove-padding-right {
389
-    height: 100%;
390
-    position: absolute;
391
-    right: -20;
392
-    top: 0;
393
-    width: 40px;
394
-}
395
-
396
-.popover-mouse-top-padding {
397
-    height: 30px;
398
-    position: absolute;
399
-    right: 0;
400
-    top: -25px;
401
-    width: 100%;
402
-}
403 381
 
404 382
 /**
405 383
  * Audio indicator on video thumbnails.

+ 1
- 0
css/main.scss Voir le fichier

@@ -69,6 +69,7 @@
69 69
 @import 'aui-components/dropdown';
70 70
 @import '404';
71 71
 @import 'policy';
72
+@import 'popover';
72 73
 @import 'filmstrip';
73 74
 @import 'unsupported-browser/main';
74 75
 @import 'modals/invite/add-people';

+ 0
- 0
react/features/base/popover/components/Popover.native.js Voir le fichier


+ 179
- 0
react/features/base/popover/components/Popover.web.js Voir le fichier

@@ -0,0 +1,179 @@
1
+import InlineDialog from '@atlaskit/inline-dialog';
2
+import React, { Component } from 'react';
3
+
4
+/**
5
+ * A map of dialog positions, relative to trigger, to css classes used to
6
+ * manipulate elements for handling mouse events.
7
+ *
8
+ * @private
9
+ * @type {object}
10
+ */
11
+const DIALOG_TO_PADDING_POSITION = {
12
+    'left': 'popover-mousemove-padding-right',
13
+    'top': 'popover-mousemove-padding-bottom'
14
+};
15
+
16
+/**
17
+ * Takes the position expected by {@code InlineDialog} and maps it to a CSS
18
+ * class that can be used styling the elements used for preventing mouseleave
19
+ * events when moving from the trigger to the dialog.
20
+ *
21
+ * @param {string} position - From which position the dialog will display.
22
+ * @private
23
+ * @returns {string}
24
+ */
25
+function _mapPositionToPaddingClass(position = 'left') {
26
+    return DIALOG_TO_PADDING_POSITION[position.split(' ')[0]];
27
+}
28
+
29
+/**
30
+ * Implements a React {@code Component} for showing an {@code InlineDialog} on
31
+ * mouseenter of the trigger and contents, and hiding the dialog on mouseleave.
32
+ *
33
+ * @extends Component
34
+ */
35
+class Popover extends Component {
36
+    /**
37
+     * Default values for {@code Popover} component's properties.
38
+     *
39
+     * @static
40
+     */
41
+    static defaultProps = {
42
+        className: '',
43
+        id: ''
44
+    };
45
+
46
+    /**
47
+     * {@code Popover} component's property types.
48
+     *
49
+     * @static
50
+     */
51
+    static propTypes = {
52
+        /**
53
+         * A child React Element to use as the trigger for showing the dialog.
54
+         */
55
+        children: React.PropTypes.object,
56
+
57
+        /**
58
+         * Additional CSS classnames to apply to the root of the {@code Popover}
59
+         * component.
60
+         */
61
+        className: React.PropTypes.string,
62
+
63
+        /**
64
+         * The ReactElement to display within the dialog.
65
+         */
66
+        content: React.PropTypes.object,
67
+
68
+        /**
69
+         * An id attribute to apply to the root of the {@code Popover}
70
+         * component.
71
+         */
72
+        id: React.PropTypes.string,
73
+
74
+        /**
75
+         * Callback to invoke when the popover has opened.
76
+         */
77
+        onPopoverOpen: React.PropTypes.func,
78
+
79
+        /**
80
+         * From which side of the dialog trigger the dialog should display. The
81
+         * value will be passed to {@code InlineDialog}.
82
+         */
83
+        position: React.PropTypes.string
84
+    };
85
+
86
+    /**
87
+     * Initializes a new {@code Popover} instance.
88
+     *
89
+     * @param {Object} props - The read-only properties with which the new
90
+     * instance is to be initialized.
91
+     */
92
+    constructor(props) {
93
+        super(props);
94
+
95
+        this.state = {
96
+            /**
97
+             * Whether or not the {@code InlineDialog} should be displayed.
98
+             *
99
+             * @type {boolean}
100
+             */
101
+            showDialog: false
102
+        };
103
+
104
+        // Bind event handlers so they are only bound once for every instance.
105
+        this._onHideDialog = this._onHideDialog.bind(this);
106
+        this._onShowDialog = this._onShowDialog.bind(this);
107
+    }
108
+
109
+    /**
110
+     * Implements React's {@link Component#render()}.
111
+     *
112
+     * @inheritdoc
113
+     * @returns {ReactElement}
114
+     */
115
+    render() {
116
+        return (
117
+            <div
118
+                className = { this.props.className }
119
+                id = { this.props.id }
120
+                onMouseEnter = { this._onShowDialog }
121
+                onMouseLeave = { this._onHideDialog }>
122
+                <InlineDialog
123
+                    content = { this._renderContent() }
124
+                    isOpen = { this.state.showDialog }
125
+                    position = { this.props.position }>
126
+                    { this.props.children }
127
+                </InlineDialog>
128
+            </div>
129
+        );
130
+    }
131
+
132
+    /**
133
+     * Stops displaying the {@code InlineDialog}.
134
+     *
135
+     * @private
136
+     * @returns {void}
137
+     */
138
+    _onHideDialog() {
139
+        this.setState({ showDialog: false });
140
+    }
141
+
142
+    /**
143
+     * Displays the {@code InlineDialog} and calls any registered onPopoverOpen
144
+     * callbacks.
145
+     *
146
+     * @private
147
+     * @returns {void}
148
+     */
149
+    _onShowDialog() {
150
+        this.setState({ showDialog: true });
151
+
152
+        if (this.props.onPopoverOpen) {
153
+            this.props.onPopoverOpen();
154
+        }
155
+
156
+    }
157
+
158
+    /**
159
+     * Renders the React Element to be displayed in the {@code InlineDialog}.
160
+     * Also adds padding to support moving the mouse from the trigger to the
161
+     * dialog to prevent mouseleave events.
162
+     *
163
+     * @private
164
+     * @returns {ReactElement}
165
+     */
166
+    _renderContent() {
167
+        const { content, position } = this.props;
168
+
169
+        return (
170
+            <div className = 'popover'>
171
+                { content }
172
+                <div className = 'popover-mouse-padding-top' />
173
+                <div className = { _mapPositionToPaddingClass(position) } />
174
+            </div>
175
+        );
176
+    }
177
+}
178
+
179
+export default Popover;

+ 1
- 0
react/features/base/popover/components/index.js Voir le fichier

@@ -0,0 +1 @@
1
+export { default as Popover } from './Popover';

+ 1
- 0
react/features/base/popover/index.js Voir le fichier

@@ -0,0 +1 @@
1
+export * from './components';

+ 21
- 59
react/features/connection-indicator/components/ConnectionIndicator.js Voir le fichier

@@ -1,7 +1,7 @@
1
-import { default as Popover } from '@atlaskit/inline-dialog';
2 1
 import React, { Component } from 'react';
3 2
 
4 3
 import { JitsiParticipantConnectionStatus } from '../../base/lib-jitsi-meet';
4
+import { Popover } from '../../base/popover';
5 5
 import { ConnectionStatsTable } from '../../connection-stats';
6 6
 
7 7
 import statsEmitter from '../statsEmitter';
@@ -123,8 +123,6 @@ class ConnectionIndicator extends Component {
123 123
         };
124 124
 
125 125
         // Bind event handlers so they are only bound once for every instance.
126
-        this._onHideStats = this._onHideStats.bind(this);
127
-        this._onShowStats = this._onShowStats.bind(this);
128 126
         this._onStatsUpdated = this._onStatsUpdated.bind(this);
129 127
         this._onToggleShowMore = this._onToggleShowMore.bind(this);
130 128
     }
@@ -174,48 +172,21 @@ class ConnectionIndicator extends Component {
174 172
      */
175 173
     render() {
176 174
         return (
177
-            <div
175
+            <Popover
178 176
                 className = 'indicator-container'
179
-                onMouseEnter = { this._onShowStats }
180
-                onMouseLeave = { this._onHideStats }>
181
-                <Popover
182
-                    content = { this._renderStatisticsTable() }
183
-                    isOpen = { this.state.showStats }
184
-                    position = { this.props.statsPopoverPosition }>
185
-                    <div className = 'popover-trigger'>
186
-                        <div className = 'connection-indicator indicator'>
187
-                            <div className = 'connection indicatoricon'>
188
-                                { this._renderIcon() }
189
-                            </div>
177
+                content = { this._renderStatisticsTable() }
178
+                position = { this.props.statsPopoverPosition }>
179
+                <div className = 'popover-trigger'>
180
+                    <div className = 'connection-indicator indicator'>
181
+                        <div className = 'connection indicatoricon'>
182
+                            { this._renderIcon() }
190 183
                         </div>
191 184
                     </div>
192
-                </Popover>
193
-            </div>
185
+                </div>
186
+            </Popover>
194 187
         );
195 188
     }
196 189
 
197
-    /**
198
-     * Sets the state not to show the Statistics Table popover.
199
-     *
200
-     * @private
201
-     * @returns {void}
202
-     */
203
-    _onHideStats() {
204
-        this.setState({ showStats: false });
205
-    }
206
-
207
-    /**
208
-     * Sets the state to show the Statistics Table popover.
209
-     *
210
-     * @private
211
-     * @returns {void}
212
-     */
213
-    _onShowStats() {
214
-        if (this.props.enableStatsDisplay) {
215
-            this.setState({ showStats: true });
216
-        }
217
-    }
218
-
219 190
     /**
220 191
      * Callback invoked when new connection stats associated with the passed in
221 192
      * user ID are available. Will update the component's display of current
@@ -295,9 +266,7 @@ class ConnectionIndicator extends Component {
295 266
     }
296 267
 
297 268
     /**
298
-     * Creates a {@code ConnectionStatisticsTable} instance and an empty div
299
-     * for preventing mouseleave events when moving from the icon to the
300
-     * popover.
269
+     * Creates a {@code ConnectionStatisticsTable} instance.
301 270
      *
302 271
      * @returns {ReactElement}
303 272
      */
@@ -312,23 +281,16 @@ class ConnectionIndicator extends Component {
312 281
         } = this.state.stats;
313 282
 
314 283
         return (
315
-            <div>
316
-                <ConnectionStatsTable
317
-                    bandwidth = { bandwidth }
318
-                    bitrate = { bitrate }
319
-                    framerate = { framerate }
320
-                    isLocalVideo = { this.props.isLocalVideo }
321
-                    onShowMore = { this._onToggleShowMore }
322
-                    packetLoss = { packetLoss }
323
-                    resolution = { resolution }
324
-                    shouldShowMore = { this.state.showMoreStats }
325
-                    transport = { transport } />
326
-                <div className = 'popover-mouse-top-padding' />
327
-                <div
328
-                    className = { interfaceConfig.VERTICAL_FILMSTRIP
329
-                        ? 'popover-mousemove-padding-right'
330
-                        : 'popover-mousemove-padding-bottom' } />
331
-            </div>
284
+            <ConnectionStatsTable
285
+                bandwidth = { bandwidth }
286
+                bitrate = { bitrate }
287
+                framerate = { framerate }
288
+                isLocalVideo = { this.props.isLocalVideo }
289
+                onShowMore = { this._onToggleShowMore }
290
+                packetLoss = { packetLoss }
291
+                resolution = { resolution }
292
+                shouldShowMore = { this.state.showMoreStats }
293
+                transport = { transport } />
332 294
         );
333 295
     }
334 296
 }

+ 18
- 46
react/features/remote-video-menu/components/RemoteVideoMenuTriggerButton.js Voir le fichier

@@ -1,4 +1,3 @@
1
-import { default as Popover } from '@atlaskit/inline-dialog';
2 1
 import React, { Component } from 'react';
3 2
 
4 3
 import {
@@ -9,6 +8,8 @@ import {
9 8
     VolumeSlider
10 9
 } from './';
11 10
 
11
+import { Popover } from '../../base/popover';
12
+
12 13
 declare var $: Object;
13 14
 declare var interfaceConfig: Object;
14 15
 
@@ -73,10 +74,6 @@ class RemoteVideoMenuTriggerButton extends Component {
73 74
     constructor(props) {
74 75
         super(props);
75 76
 
76
-        this.state = {
77
-            showRemoteMenu: false
78
-        };
79
-
80 77
         /**
81 78
          * The internal reference to topmost DOM/HTML element backing the React
82 79
          * {@code Component}. Accessed directly for associating an element as
@@ -87,8 +84,7 @@ class RemoteVideoMenuTriggerButton extends Component {
87 84
          */
88 85
         this._rootElement = null;
89 86
 
90
-        // Bind event handlers so they are only bound once for every instance.
91
-        this._onHideRemoteMenu = this._onHideRemoteMenu.bind(this);
87
+        // Bind event handler so it is only bound once for every instance.
92 88
         this._onShowRemoteMenu = this._onShowRemoteMenu.bind(this);
93 89
     }
94 90
 
@@ -106,35 +102,21 @@ class RemoteVideoMenuTriggerButton extends Component {
106 102
         }
107 103
 
108 104
         return (
109
-            <div
110
-                onMouseEnter = { this._onShowRemoteMenu }
111
-                onMouseLeave = { this._onHideRemoteMenu }>
112
-                <Popover
113
-                    content = { content }
114
-                    isOpen = { this.state.showRemoteMenu }
115
-                    position = { interfaceConfig.VERTICAL_FILMSTRIP
116
-                        ? 'left middle' : 'top center' }>
117
-                    <span
118
-                        className = 'popover-trigger remote-video-menu-trigger'>
119
-                        <i
120
-                            className = 'icon-thumb-menu'
121
-                            title = 'Remote user controls' />
122
-                    </span>
123
-                </Popover>
124
-            </div>
105
+            <Popover
106
+                content = { content }
107
+                onPopoverOpen = { this._onShowRemoteMenu }
108
+                position = { interfaceConfig.VERTICAL_FILMSTRIP
109
+                    ? 'left middle' : 'top center' }>
110
+                <span
111
+                    className = 'popover-trigger remote-video-menu-trigger'>
112
+                    <i
113
+                        className = 'icon-thumb-menu'
114
+                        title = 'Remote user controls' />
115
+                </span>
116
+            </Popover>
125 117
         );
126 118
     }
127 119
 
128
-    /**
129
-     * Closes the {@code RemoteVideoMenu}.
130
-     *
131
-     * @private
132
-     * @returns {void}
133
-     */
134
-    _onHideRemoteMenu() {
135
-        this.setState({ showRemoteMenu: false });
136
-    }
137
-
138 120
     /**
139 121
      * Opens the {@code RemoteVideoMenu}.
140 122
      *
@@ -143,8 +125,6 @@ class RemoteVideoMenuTriggerButton extends Component {
143 125
      */
144 126
     _onShowRemoteMenu() {
145 127
         this.props.onMenuDisplay();
146
-
147
-        this.setState({ showRemoteMenu: true });
148 128
     }
149 129
 
150 130
     /**
@@ -172,13 +152,11 @@ class RemoteVideoMenuTriggerButton extends Component {
172 152
                 <MuteButton
173 153
                     isAudioMuted = { isAudioMuted }
174 154
                     key = 'mute'
175
-                    onClick = { this._onHideRemoteMenu }
176 155
                     participantID = { participantID } />
177 156
             );
178 157
             buttons.push(
179 158
                 <KickButton
180 159
                     key = 'kick'
181
-                    onClick = { this._onHideRemoteMenu }
182 160
                     participantID = { participantID } />
183 161
             );
184 162
         }
@@ -204,15 +182,9 @@ class RemoteVideoMenuTriggerButton extends Component {
204 182
 
205 183
         if (buttons.length > 0) {
206 184
             return (
207
-                <div>
208
-                    <RemoteVideoMenu id = { participantID }>
209
-                        { buttons }
210
-                    </RemoteVideoMenu>
211
-                    <div
212
-                        className = { interfaceConfig.VERTICAL_FILMSTRIP
213
-                            ? 'popover-mousemove-padding-right'
214
-                            : 'popover-mousemove-padding-bottom' } />
215
-                </div>
185
+                <RemoteVideoMenu id = { participantID }>
186
+                    { buttons }
187
+                </RemoteVideoMenu>
216 188
             );
217 189
         }
218 190
 

+ 11
- 63
react/features/video-quality/components/VideoQualityLabel.web.js Voir le fichier

@@ -1,9 +1,8 @@
1
-import { default as Popover } from '@atlaskit/inline-dialog';
2 1
 import React, { Component } from 'react';
3 2
 import { connect } from 'react-redux';
4 3
 
5 4
 import { translate } from '../../base/i18n';
6
-
5
+import { Popover } from '../../base/popover';
7 6
 import { VideoQualityDialog } from './';
8 7
 
9 8
 import {
@@ -89,13 +88,6 @@ export class VideoQualityLabel extends Component {
89 88
         super(props);
90 89
 
91 90
         this.state = {
92
-            /**
93
-             * Whether or not the {@code VideoQualityDialog} is displayed.
94
-             *
95
-             * @type {boolean}
96
-             */
97
-            showVideoQualityDialog: false,
98
-
99 91
             /**
100 92
              * Whether or not the filmstrip is transitioning from not visible
101 93
              * to visible. Used to set a transition class for animation.
@@ -104,10 +96,6 @@ export class VideoQualityLabel extends Component {
104 96
              */
105 97
             togglingToVisible: false
106 98
         };
107
-
108
-        // Bind event handlers so they are only bound once for every instance.
109
-        this._onHideQualityDialog = this._onHideQualityDialog.bind(this);
110
-        this._onShowQualityDialog = this._onShowQualityDialog.bind(this);
111 99
     }
112 100
 
113 101
     /**
@@ -161,23 +149,18 @@ export class VideoQualityLabel extends Component {
161 149
             = `${baseClasses} ${filmstrip} ${remoteVideosVisible} ${opening}`;
162 150
 
163 151
         return (
164
-            <div
152
+            <Popover
165 153
                 className = { classNames }
154
+                content = { <VideoQualityDialog /> }
166 155
                 id = 'videoResolutionLabel'
167
-                onMouseEnter = { this._onShowQualityDialog }
168
-                onMouseLeave = { this._onHideQualityDialog }>
169
-                <Popover
170
-                    content = { this._renderQualityDialog() }
171
-                    isOpen = { this.state.showVideoQualityDialog }
172
-                    position = { 'left top' }>
173
-                    <div
174
-                        className = 'video-quality-label-status'>
175
-                        { _audioOnly
176
-                            ? <i className = 'icon-visibility-off' />
177
-                            : this._mapResolutionToTranslation(_resolution) }
178
-                    </div>
179
-                </Popover>
180
-            </div>
156
+                position = { 'left top' }>
157
+                <div
158
+                    className = 'video-quality-label-status'>
159
+                    { _audioOnly
160
+                        ? <i className = 'icon-visibility-off' />
161
+                        : this._mapResolutionToTranslation(_resolution) }
162
+                </div>
163
+            </Popover>
181 164
         );
182 165
     }
183 166
 
@@ -209,41 +192,6 @@ export class VideoQualityLabel extends Component {
209 192
         return this.props.t(
210 193
             RESOLUTION_TO_TRANSLATION_KEY[highestMatchingResolution]);
211 194
     }
212
-
213
-    /**
214
-     * Shows the {@code VideoQualityDialog}.
215
-     *
216
-     * @private
217
-     * @returns {void}
218
-     */
219
-    _onShowQualityDialog() {
220
-        this.setState({ showVideoQualityDialog: true });
221
-    }
222
-
223
-    /**
224
-     * Hides the {@code VideoQualityDialog}.
225
-     *
226
-     * @private
227
-     * @returns {void}
228
-     */
229
-    _onHideQualityDialog() {
230
-        this.setState({ showVideoQualityDialog: false });
231
-    }
232
-
233
-    /**
234
-     * Returns a React Element for choosing a maximum receive video quality.
235
-     *
236
-     * @private
237
-     * @returns {ReactElement}
238
-     */
239
-    _renderQualityDialog() {
240
-        return (
241
-            <div>
242
-                <VideoQualityDialog />
243
-                <div className = 'popover-mousemove-padding-right' />
244
-            </div>
245
-        );
246
-    }
247 195
 }
248 196
 
249 197
 /**

Chargement…
Annuler
Enregistrer