Bug 1131581: move screensharing options into a dropdown anchored to the screenshare toolbar button. r=Standard8

This commit is contained in:
Mike de Boer 2015-02-17 15:03:59 +01:00
Родитель 4bb94f35b0
Коммит ad14612ec0
8 изменённых файлов: 153 добавлений и 29 удалений

Просмотреть файл

@ -17,6 +17,8 @@
/* desktop version */
.fx-embedded .conversation-toolbar {
/* required to have dropdowns float atop the .room-invitation-overlay: */
z-index: 1020;
position: absolute;
top: 0;
left: 0;
@ -40,13 +42,13 @@
height: 100%;
}
.conversation-toolbar li {
.conversation-toolbar > li {
float: left;
font-size: 0; /* prevents vertical bottom padding added to buttons in google
chrome */
}
.standalone .conversation-toolbar li {
.standalone .conversation-toolbar > li {
/* XXX Might make sense to use relative units here.
*/
margin-right: 16px;
@ -165,7 +167,8 @@
background-color: transparent;
opacity: 1;
}
.conversation-toolbar .transparent-button:hover {
.conversation-toolbar .transparent-button:hover,
.conversation-toolbar .transparent-button.menu-showing {
background-color: rgba(255,255,255,.35);
opacity: 1;
}
@ -208,9 +211,15 @@
/* Screen share button */
.btn-screen-share {
/* XXX Replace this with the real button: bug 1126286 */
position: relative;
background-image: url(../img/icons-16x16.svg#screen-white);
background-size: 16px 16px;
width: 42px;
}
/* Make room for the chevron. */
.conversation-toolbar .btn-screen-share:not(.active) {
background-position: 5px center;
}
.btn-screen-share.active {
@ -220,7 +229,21 @@
}
.btn-screen-share.disabled {
/* XXX Add css here for disabled state: bug 1126286 */
background-image: url(../img/icons-16x16.svg#screen-disabled);
}
.btn-screen-share .chevron {
background-image: url(../img/icons-10x10.svg#dropdown-white);
background-size: 10px 10px;
position: absolute;
right: 2px;
top: 8px;
width: 10px;
height: 10px;
}
.btn-screen-share.disabled .chevron {
background-image: url(../img/icons-10x10.svg#dropdown-disabled);
}
.fx-embedded .remote_wrapper {
@ -364,9 +387,10 @@
background-color: #111;
}
.conversation-window-dropdown li {
.conversation-window-dropdown > li {
padding: 2px;
font-size: .9em;
font-size: .7rem;
white-space: nowrap;
}
/* Expired call url page */

Просмотреть файл

@ -26,7 +26,11 @@ use[id$="-active"] {
}
use[id$="-white"] {
fill: rgba(255, 255, 255, 0.8);
fill: rgba(255,255,255,0.8);
}
use[id$="-disabled"] {
fill: rgba(255,255,255,0.4);
}
</style>
<defs style="display:none">

До

Ширина:  |  Высота:  |  Размер: 2.0 KiB

После

Ширина:  |  Высота:  |  Размер: 2.0 KiB

Просмотреть файл

@ -32,6 +32,10 @@ use[id$="-red"] {
use[id$="-white"] {
fill: #fff;
}
use[id$="-disabled"] {
fill: rgba(255,255,255,.6);
}
</style>
<defs style="display:none">
<path id="audio-shape" fill-rule="evenodd" clip-rule="evenodd" d="M11.429,6.857v2.286c0,1.894-1.535,3.429-3.429,3.429
@ -181,5 +185,6 @@ use[id$="-white"] {
<use id="video-active" xlink:href="#video-shape"/>
<use id="tour" xlink:href="#tour-shape"/>
<use id="screen-white" xlink:href="#screen-shape"/>
<use id="screen-disabled" xlink:href="#screen-shape"/>
<use id="screenmute-white" xlink:href="#screenmute-shape"/>
</svg>

До

Ширина:  |  Высота:  |  Размер: 12 KiB

После

Ширина:  |  Высота:  |  Размер: 12 KiB

Просмотреть файл

@ -101,6 +101,31 @@ loop.shared.mixins = (function() {
componentDidMount: function() {
this.documentBody.addEventListener("click", this._onBodyClick);
this.documentBody.addEventListener("blur", this.hideDropdownMenu);
var menu = this.refs.menu;
if (!menu) {
return;
}
// Correct the position of the menu if necessary.
var menuNode = menu.getDOMNode();
var menuNodeRect = menuNode.getBoundingClientRect();
var bodyRect = {
height: this.documentBody.offsetHeight,
width: this.documentBody.offsetWidth
};
// First we check the vertical overflow.
var y = menuNodeRect.top + menuNodeRect.height;
if (y >= bodyRect.height) {
menuNode.style.marginTop = bodyRect.height - y + "px";
}
// Then we check the horizontal overflow.
var x = menuNodeRect.left + menuNodeRect.width;
if (x >= bodyRect.width) {
menuNode.style.marginLeft = bodyRect.width - x + "px";
}
},
componentWillUnmount: function() {

Просмотреть файл

@ -85,6 +85,8 @@ loop.shared.views = (function(_, l10n) {
* loop.shared.utils.SCREEN_SHARE_STATES
*/
var ScreenShareControlButton = React.createClass({displayName: "ScreenShareControlButton",
mixins: [sharedMixins.DropdownMenuMixin],
propTypes: {
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
visible: React.PropTypes.bool.isRequired,
@ -96,11 +98,14 @@ loop.shared.views = (function(_, l10n) {
this.props.dispatcher.dispatch(
new sharedActions.EndScreenShare({}));
} else {
this.props.dispatcher.dispatch(
new sharedActions.StartScreenShare({}));
this.toggleDropdownMenu();
}
},
_handleShareWindows: function() {
this.props.dispatcher.dispatch(new sharedActions.StartScreenShare({}));
},
_getTitle: function() {
var prefix = this.props.state === SCREEN_SHARE_STATES.ACTIVE ?
"active" : "inactive";
@ -113,18 +118,36 @@ loop.shared.views = (function(_, l10n) {
return null;
}
var screenShareClasses = React.addons.classSet({
var cx = React.addons.classSet;
var isActive = this.props.state === SCREEN_SHARE_STATES.ACTIVE;
var screenShareClasses = cx({
"btn": true,
"btn-screen-share": true,
"transparent-button": true,
"active": this.props.state === SCREEN_SHARE_STATES.ACTIVE,
"menu-showing": this.state.showMenu,
"active": isActive,
"disabled": this.props.state === SCREEN_SHARE_STATES.PENDING
});
var dropdownMenuClasses = cx({
"native-dropdown-menu": true,
"conversation-window-dropdown": true,
"visually-hidden": !this.state.showMenu
});
return (
React.createElement("button", {className: screenShareClasses,
onClick: this.handleClick,
title: this._getTitle()})
React.createElement("div", null,
React.createElement("button", {className: screenShareClasses,
onClick: this.handleClick,
title: this._getTitle()},
isActive ? null : React.createElement("span", {className: "chevron"})
),
React.createElement("ul", {ref: "menu", className: dropdownMenuClasses},
React.createElement("li", {onClick: this._handleShareWindows},
l10n.get("share_windows_button_title")
)
)
)
);
}
});

Просмотреть файл

@ -85,6 +85,8 @@ loop.shared.views = (function(_, l10n) {
* loop.shared.utils.SCREEN_SHARE_STATES
*/
var ScreenShareControlButton = React.createClass({
mixins: [sharedMixins.DropdownMenuMixin],
propTypes: {
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
visible: React.PropTypes.bool.isRequired,
@ -96,11 +98,14 @@ loop.shared.views = (function(_, l10n) {
this.props.dispatcher.dispatch(
new sharedActions.EndScreenShare({}));
} else {
this.props.dispatcher.dispatch(
new sharedActions.StartScreenShare({}));
this.toggleDropdownMenu();
}
},
_handleShareWindows: function() {
this.props.dispatcher.dispatch(new sharedActions.StartScreenShare({}));
},
_getTitle: function() {
var prefix = this.props.state === SCREEN_SHARE_STATES.ACTIVE ?
"active" : "inactive";
@ -113,18 +118,36 @@ loop.shared.views = (function(_, l10n) {
return null;
}
var screenShareClasses = React.addons.classSet({
var cx = React.addons.classSet;
var isActive = this.props.state === SCREEN_SHARE_STATES.ACTIVE;
var screenShareClasses = cx({
"btn": true,
"btn-screen-share": true,
"transparent-button": true,
"active": this.props.state === SCREEN_SHARE_STATES.ACTIVE,
"menu-showing": this.state.showMenu,
"active": isActive,
"disabled": this.props.state === SCREEN_SHARE_STATES.PENDING
});
var dropdownMenuClasses = cx({
"native-dropdown-menu": true,
"conversation-window-dropdown": true,
"visually-hidden": !this.state.showMenu
});
return (
<button className={screenShareClasses}
onClick={this.handleClick}
title={this._getTitle()}></button>
<div>
<button className={screenShareClasses}
onClick={this.handleClick}
title={this._getTitle()}>
{isActive ? null : <span className="chevron"/>}
</button>
<ul ref="menu" className={dropdownMenuClasses}>
<li onClick={this._handleShareWindows}>
{l10n.get("share_windows_button_title")}
</li>
</ul>
</div>
);
}
});

Просмотреть файл

@ -118,8 +118,9 @@ describe("loop.shared.views", function() {
state: SCREEN_SHARE_STATES.PENDING
}));
expect(comp.getDOMNode().classList.contains("active")).eql(false);
expect(comp.getDOMNode().classList.contains("disabled")).eql(true);
var node = comp.getDOMNode().querySelector(".btn-screen-share");
expect(node.classList.contains("active")).eql(false);
expect(node.classList.contains("disabled")).eql(true);
});
it("should render an active share button", function() {
@ -130,11 +131,12 @@ describe("loop.shared.views", function() {
state: SCREEN_SHARE_STATES.ACTIVE
}));
expect(comp.getDOMNode().classList.contains("active")).eql(true);
expect(comp.getDOMNode().classList.contains("disabled")).eql(false);
var node = comp.getDOMNode().querySelector(".btn-screen-share");
expect(node.classList.contains("active")).eql(true);
expect(node.classList.contains("disabled")).eql(false);
});
it("should dispatch a StartScreenShare action on click when the state is not active",
it("should show the screenshare dropdown on click when the state is not active",
function() {
var comp = TestUtils.renderIntoDocument(
React.createElement(sharedViews.ScreenShareControlButton, {
@ -143,7 +145,24 @@ describe("loop.shared.views", function() {
state: SCREEN_SHARE_STATES.INACTIVE
}));
TestUtils.Simulate.click(comp.getDOMNode());
expect(comp.state.showMenu).eql(false);
TestUtils.Simulate.click(comp.getDOMNode().querySelector(".btn-screen-share"));
expect(comp.state.showMenu).eql(true);
});
it("should dispatch a StartScreenShare action on option click in screenshare dropdown",
function() {
var comp = TestUtils.renderIntoDocument(
React.createElement(sharedViews.ScreenShareControlButton, {
dispatcher: dispatcher,
visible: true,
state: SCREEN_SHARE_STATES.INACTIVE
}));
TestUtils.Simulate.click(comp.getDOMNode().querySelector(
".conversation-window-dropdown > li"));
sinon.assert.calledOnce(dispatcher.dispatch);
sinon.assert.calledWithExactly(dispatcher.dispatch,
@ -159,7 +178,7 @@ describe("loop.shared.views", function() {
state: SCREEN_SHARE_STATES.ACTIVE
}));
TestUtils.Simulate.click(comp.getDOMNode());
TestUtils.Simulate.click(comp.getDOMNode().querySelector(".btn-screen-share"));
sinon.assert.calledOnce(dispatcher.dispatch);
sinon.assert.calledWithExactly(dispatcher.dispatch,

Просмотреть файл

@ -185,6 +185,7 @@ mute_local_video_button_title=Mute your video
unmute_local_video_button_title=Unmute your video
active_screenshare_button_title=Stop sharing
inactive_screenshare_button_title=Share your screen
share_windows_button_title=Share other Windows
## LOCALIZATION NOTE (call_with_contact_title): The title displayed
## when calling a contact. Don't translate the part between {{..}} because