Bug 1042799: Loop UI components showcase, r=mikedeboer

This commit is contained in:
Nicolas Perriault 2014-07-29 11:23:31 +01:00
parent a642361aa4
commit c4150737a6
14 changed files with 414 additions and 307 deletions

View File

@ -24,7 +24,7 @@ loop.conversation = (function(OT, mozL10n) {
var IncomingCallView = React.createClass({displayName: 'IncomingCallView',
propTypes: {
model: React.PropTypes.func.isRequired
model: React.PropTypes.object.isRequired
},
getInitialState: function() {

View File

@ -24,7 +24,7 @@ loop.conversation = (function(OT, mozL10n) {
var IncomingCallView = React.createClass({
propTypes: {
model: React.PropTypes.func.isRequired
model: React.PropTypes.object.isRequired
},
getInitialState: function() {

View File

@ -8,7 +8,7 @@
position: relative;
}
.conversation .controls {
.conversation-toolbar {
position: absolute;
z-index: 999; /* required to have it superimposed to the video element */
left: 0;
@ -20,13 +20,13 @@
padding: 0;
}
.conversation .controls li {
.conversation-toolbar li {
float: left;
font-size: 0px; /* prevents vertical bottom padding added to buttons in google
chrome */
}
.conversation .controls .btn {
.conversation-toolbar .btn {
width: 40px;
height: 30px;
background: transparent;
@ -38,66 +38,66 @@
cursor: pointer;
}
.conversation .controls .btn:hover {
.conversation-toolbar .btn:hover {
background-color: rgba(255, 255, 255, .35);
}
/* Hangup button */
.conversation .controls .btn-hangup {
.conversation-toolbar .btn-hangup {
background-color: #D74345;
background-image: url(../img/hangup-inverse-14x14.png);
}
.conversation .controls .btn-hangup:hover {
.conversation-toolbar .btn-hangup:hover {
background-color: #C53436;
}
@media (min-resolution: 2dppx) {
.conversation .controls .btn-hangup {
.conversation-toolbar .btn-hangup {
background-image: url(../img/hangup-inverse-14x14@2x.png);
}
}
/* Common media control buttons behavior */
.conversation .controls .media-control {
.conversation-toolbar .media-control {
background-color: transparent;
opacity: 1;
}
.conversation .controls .media-control:hover {
.conversation-toolbar .media-control:hover {
background-color: rgba(255, 255, 255, .35);
opacity: 1;
}
.conversation .controls .media-control.muted {
.conversation-toolbar .media-control.muted {
background-color: #0096DD;
opacity: 1;
}
/* Audio mute button */
.conversation .controls .local-media.btn-mute-audio {
.conversation-toolbar .local-media.btn-mute-audio {
background-image: url(../img/audio-inverse-14x14.png);
}
.conversation .controls .local-media.btn-mute-audio.muted {
.conversation-toolbar .local-media.btn-mute-audio.muted {
background-image: url(../img/mute-inverse-14x14.png);
}
@media (min-resolution: 2dppx) {
.conversation .controls .local-media.btn-mute-audio {
.conversation-toolbar .local-media.btn-mute-audio {
background-image: url(../img/audio-inverse-14x14@2x.png);
}
.conversation .controls .local-media.btn-mute-audio.muted {
.conversation-toolbar .local-media.btn-mute-audio.muted {
background-image: url(../img/mute-inverse-14x14@2x.png);
}
}
/* Video mute button */
.conversation .controls .local-media.btn-mute-video {
.conversation-toolbar .local-media.btn-mute-video {
background-image: url(../img/video-inverse-14x14.png);
}
.conversation .controls .local-media.btn-mute-video.muted {
.conversation-toolbar .local-media.btn-mute-video.muted {
background-image: url(../img/facemute-14x14.png);
}
@media (min-resolution: 2dppx) {
.conversation .controls .local-media.btn-mute-video {
.conversation-toolbar .local-media.btn-mute-video {
background-image: url(../img/video-inverse-14x14@2x.png);
}
.conversation .controls .local-media.btn-mute-video.muted {
.conversation-toolbar .local-media.btn-mute-video.muted {
background-image: url(../img/facemute-14x14@2x.png);
}
}

View File

@ -1,274 +0,0 @@
<!DOCTYPE html>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<!-- This file is intended to help frontend developers to easily identify what
are the available styles for the Loop UI components. -->
<html>
<head>
<meta charset="utf-8">
<title>Loop UI shared CSS information/demo</title>
<link type="text/css" rel="stylesheet" href="common.css">
<link type="text/css" rel="stylesheet" href="panel.css">
<link type="text/css" rel="stylesheet" href="conversation.css">
<style>
body {
width: 600px;
margin: 1em auto;
background: #fff;
font-size: 12px;
}
h2 {
margin-top: 3em;
}
</style>
</head>
<body>
<h1>Loop UI toolkit</h1>
<h2>Logo icons</h2>
<p>
<img src="../img/icon_32.png"> 32x32 transparent PNG
<img src="../img/icon_64.png"> 64x64 transparent PNG
</p>
<p><em><strong>Note:</strong> these are temporary.</em></p>
<h2>Share panel</h2>
<h3>Simple</h3>
<div class="share">
<form class="description">
<p>This is a simple message.</p>
</form>
<div class="action">
<p><input type="url" value="http://loop.im/plop75"></p>
<p>Your name will appear as <a href="">Unnamed</a>.</p>
</div>
</div>
<h3>Featuring options</h3>
<div class="share">
<form class="description">
<p class="field">
<label>Share this link with a friend to
<select>
<option>browse together</option>
<option selected="selected">video chat</option>
<option>audio chat</option>
<option>text chat</option>
</select>
</label>
</p>
<p class="field">
<label>
Use webcam <select><option>Foo</option></select>
</label>
</p>
<p class="field">
<label>Use whatever
<select><option>Long foo is long indeed</option></select>
</label>
</p>
<p class="preview cf">
Preview <video></video>
</p>
</form>
<div class="action">
<p><input type="url" value="http://loop.im/plop75"></p>
<p>Your name will appear as <a href="">Unnamed</a>.</p>
</div>
</div>
<h2>Conversation window</h2>
<p><em>The conversation component adapts automatically to its container to
occupy all the available space.</em></p>
<h3>Large</h3>
<div class="conversation">
<div class="media nested">
<div class="remote_wrapper">
<video class="remote"></video>
</div>
<video class="local"></video>
</div>
</div>
<h3>Large with controls</h3>
<div class="conversation">
<ul class="controls">
<li><button class="btn btn-hangup" title="Hangup"></button></li>
<li><button class="btn media-control local-media btn-mute-video" title="Mute video"></button></li>
<li><button class="btn media-control local-media btn-mute-audio" title="Mute audio"></button></li>
</ul>
<div class="media nested">
<div class="remote_wrapper">
<video class="remote"></video>
</div>
<video class="local"></video>
</div>
</div>
<h3>Small (think chat window)</h3>
<div style="width: 204px">
<div class="conversation">
<ul class="controls">
<li><button class="btn btn-hangup" title="Hangup"></button></li>
<li><button class="btn media-control local-media btn-mute-video" title="Mute video"></button></li>
<li><button class="btn media-control local-media btn-mute-audio" title="Mute audio"></button></li>
</ul>
<div class="media nested">
<div class="remote_wrapper">
<video class="remote"></video>
</div>
<video class="local"></video>
</div>
</div>
</div>
<h3>Side by side</h3>
<div class="conversation">
<div class="media side-by-side">
<div class="remote_wrapper">
<video class="remote"></video>
</div>
<video class="local"></video>
</div>
</div>
<h2>Controls button variants</h2>
<h3>Nothing muted</h3>
<div style="width: 204px; min-height: 26px">
<div class="conversation">
<ul class="controls">
<li><button class="btn btn-hangup" title="Hangup"></button></li>
<li><button class="btn media-control local-media btn-mute-video" title="Mute video"></button></li>
<li><button class="btn media-control local-media btn-mute-audio" title="Mute audio"></button></li>
</ul>
</div>
</div>
<h3>Local audio muted</h3>
<div style="width: 204px; min-height: 26px">
<div class="conversation">
<ul class="controls">
<li><button class="btn btn-hangup" title="Hangup"></button></li>
<li><button class="btn media-control local-media btn-mute-video" title="Mute video"></button></li>
<li><button class="btn media-control local-media btn-mute-audio muted" title="Mute audio"></button></li>
</ul>
</div>
</div>
<h3>Local video muted</h3>
<div style="width: 204px; min-height: 26px">
<div class="conversation">
<ul class="controls">
<li><button class="btn btn-hangup" title="Hangup"></button></li>
<li><button class="btn media-control local-media btn-mute-video muted" title="Mute video"></button></li>
<li><button class="btn media-control local-media btn-mute-audio" title="Mute audio"></button></li>
</ul>
</div>
</div>
<h2>Expired call url view</h2>
<div class="expired-url-info">
<div class="info-panel">
<div class="firefox-logo"></div>
<h1 >Oops!</h1>
<h4 >This URL is unavailable.</h4>
</div>
<div class="promote-firefox">
<h3>Download Firefox to make free audio and video calls!</h3>
<p>
<a class="btn btn-large btn-success" href="https://www.mozilla.org/firefox/" data-reactid=".0.1.1.0">Get Firefox</a>
</p>
</div>
</div>
<h2>Buttons</h2>
<h3>Using <code>&lt;a&gt;</code></h3>
<p>
<a href="" class="btn">default</a>
<a href="" class="btn btn-info">info</a>
<a href="" class="btn btn-success">success</a>
<a href="" class="btn btn-warning">warning</a>
<a href="" class="btn btn-error">error</a>
</p>
<h3>Inline</h3>
<p>Click <a href="" class="btn btn-info">here</a>.</p>
<h3>Using <code>&lt;button&gt;</code></h3>
<p>
<button class="btn">default</button>
<button class="btn btn-info">info</button>
<button class="btn btn-success">success</button>
<button class="btn btn-warning">warning</button>
<button class="btn btn-error">error</button>
</p>
<h3>Large buttons</h3>
<p>
<a class="btn btn-large">default</a>
<a class="btn btn-large btn-info">info</a>
<a class="btn btn-large btn-success">success</a>
<a class="btn btn-large btn-warning">warning</a>
<a class="btn btn-large btn-error">error</a>
</p>
<h2>Alerts</h2>
<div class="alert alert-error">
<button class="close"></button>
<p class="message">Oops! Something went really wrong.</p>
</div>
<div class="alert alert-warning">
<button class="close"></button>
<p class="message">Oops! This is a warning.</p>
</div>
<h2>Logos</h2>
<h3>Centered Firefox logo</h3>
<div class="firefox-logo"></div>
<h2>Incoming call</h2>
<div class="incoming-call">
<h2>Incoming call</h2>
<p>
<button class="btn btn-success btn-accept">Accept</button>
<button class="btn btn-error btn-decline">Decline</button>
</p>
</div>
<script>
window.onload = function() {
[].forEach.call(document.querySelectorAll("video"), function(video) {
video.setAttribute("src", "http://v2v.cc/~j/theora_testsuite/320x240.ogg");
});
};
</script>
</body>
</html>

View File

@ -180,7 +180,7 @@ loop.shared.views = (function(_, OT, l10n) {
render: function() {
/* jshint ignore:start */
return (
React.DOM.ul({className: "controls"},
React.DOM.ul({className: "conversation-toolbar"},
React.DOM.li(null, React.DOM.button({className: "btn btn-hangup",
onClick: this.handleClickHangup,
title: __("hangup_button_title")})),

View File

@ -180,7 +180,7 @@ loop.shared.views = (function(_, OT, l10n) {
render: function() {
/* jshint ignore:start */
return (
<ul className="controls">
<ul className="conversation-toolbar">
<li><button className="btn btn-hangup"
onClick={this.handleClickHangup}
title={__("hangup_button_title")}></button></li>

View File

@ -42,14 +42,14 @@ loop.webapp = (function($, _, OT, webL10n) {
render: function() {
if (this.props.helper.isFirefox(navigator.userAgent)) {
return React.DOM.div(null );
return React.DOM.div(null);
}
return (
React.DOM.div( {className:"promote-firefox"},
React.DOM.h3(null, __("promote_firefox_hello_heading")),
React.DOM.div({className: "promote-firefox"},
React.DOM.h3(null, __("promote_firefox_hello_heading")),
React.DOM.p(null,
React.DOM.a( {className:"btn btn-large btn-success",
href:"https://www.mozilla.org/firefox/"},
React.DOM.a({className: "btn btn-large btn-success",
href: "https://www.mozilla.org/firefox/"},
__("get_firefox_button")
)
)
@ -69,13 +69,13 @@ loop.webapp = (function($, _, OT, webL10n) {
render: function() {
/* jshint ignore:start */
return (
React.DOM.div( {className:"expired-url-info"},
React.DOM.div( {className:"info-panel"},
React.DOM.div( {className:"firefox-logo"} ),
React.DOM.h1(null, __("call_url_unavailable_notification_heading")),
React.DOM.div({className: "expired-url-info"},
React.DOM.div({className: "info-panel"},
React.DOM.div({className: "firefox-logo"}),
React.DOM.h1(null, __("call_url_unavailable_notification_heading")),
React.DOM.h4(null, __("call_url_unavailable_notification_message"))
),
PromoteFirefoxView( {helper:this.props.helper} )
),
PromoteFirefoxView({helper: this.props.helper})
)
);
/* jshint ignore:end */

View File

@ -0,0 +1,10 @@
Loop UI Components Showcase
===========================
This app file showcases all Loop's view components.
If you want to modify the app, launch the following command:
browser/components/loop/build-jsx --watch
And start editing the `ui-showcase.jsx` file.

View File

@ -0,0 +1,14 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* /!\ FIXME: THIS IS A HORRID HACK which fakes both the mozL10n and webL10n
* objects and makes them returning "fake string" for any requested string id.
* @type {Object}
*/
document.webL10n = document.mozL10n = {
get: function() {
return "fake text";
}
};

View File

@ -0,0 +1,11 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Faking the mozLoop object which doesn't exist in regular web pages.
* @type {Object}
*/
navigator.mozLoop = {
getLoopCharPref: function() {}
};

View File

@ -0,0 +1,33 @@
<!DOCTYPE html>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<html>
<head>
<meta charset="utf-8">
<title>Loop UI Components Showcase</title>
<link rel="stylesheet" type="text/css" href="../content/shared/css/common.css">
<link rel="stylesheet" type="text/css" href="../content/shared/css/conversation.css">
<link rel="stylesheet" type="text/css" href="../content/shared/css/panel.css">
<link rel="stylesheet" type="text/css" href="ui-showcase.css">
</head>
<body>
<div id="main"></div>
<script src="fake-mozLoop.js"></script>
<script src="fake-l10n.js"></script>
<script src="../content/libs/sdk.js"></script>
<script src="../content/shared/libs/react-0.10.0.js"></script>
<script src="../content/shared/libs/jquery-2.1.0.js"></script>
<script src="../content/shared/libs/lodash-2.4.1.js"></script>
<script src="../content/shared/libs/backbone-1.1.2.js"></script>
<script src="../content/shared/js/models.js"></script>
<script src="../content/shared/js/router.js"></script>
<script src="../content/shared/js/views.js"></script>
<script src="../content/js/client.js"></script>
<script src="../content/js/desktopRouter.js"></script>
<script src="../standalone/content/js/webapp.js"></script>
<script src="../content/js/panel.js"></script>
<script src="../content/js/conversation.js"></script>
<script src="ui-showcase.js"></script>
</body>
</html>

View File

@ -0,0 +1,47 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
.showcase {
width: 730px;
margin: 0 auto;
}
.showcase > header {
position: fixed;
top: 0;
background-color: #fbfbfb;
z-index: 1000;
width: 100%;
padding-bottom: 1em;
}
.showcase .menu > a {
margin-right: .5em;
}
.showcase > section {
position: relative;
padding-top: 10em;
clear: both;
}
.showcase > section > h1 {
border-bottom: 1px solid #aaa;
}
.showcase > section .comp {
margin: 0 auto; /* width is usually set programmatically */
}
.showcase > section .comp.dashed {
border: 1px dashed #ccc;
}
.showcase > section > .example {
margin-bottom: 6em;
}
.showcase > section .example > h3 {
border-bottom: 1px dashed #aaa;
}

View File

@ -0,0 +1,133 @@
/** @jsx React.DOM */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* jshint newcap:false */
/* global loop:true, React */
(function() {
"use strict";
// 1. Desktop components
// 1.1 Panel
var PanelView = loop.panel.PanelView;
// 1.2. Conversation Window
var IncomingCallView = loop.conversation.IncomingCallView;
// 2. Standalone webapp
var CallUrlExpiredView = loop.webapp.CallUrlExpiredView;
// 3. Shared components
var ConversationToolbar = loop.shared.views.ConversationToolbar;
var ConversationView = loop.shared.views.ConversationView;
// Local helpers
function returnTrue() {
return true;
}
function returnFalse() {
return false;
}
var Example = React.createClass({displayName: 'Example',
render: function() {
var cx = React.addons.classSet;
return (
React.DOM.div({className: "example"},
React.DOM.h3(null, this.props.summary),
React.DOM.div({className: cx({comp: true, dashed: this.props.dashed}),
style: this.props.style || {}},
this.props.children
)
)
);
}
});
var Section = React.createClass({displayName: 'Section',
render: function() {
return (
React.DOM.section({id: this.props.name},
React.DOM.h1(null, this.props.name),
this.props.children
)
);
}
});
var ShowCase = React.createClass({displayName: 'ShowCase',
render: function() {
return (
React.DOM.div({className: "showcase"},
React.DOM.header(null,
React.DOM.h1(null, "Loop UI Components Showcase"),
React.DOM.nav({className: "menu"},
React.Children.map(this.props.children, function(section) {
return (
React.DOM.a({className: "btn btn-info", href: "#" + section.props.name},
section.props.name
)
);
})
)
),
this.props.children
)
);
}
});
var App = React.createClass({displayName: 'App',
render: function() {
return (
ShowCase(null,
Section({name: "PanelView"},
Example({summary: "332px wide", dashed: "true", style: {width: "332px"}},
PanelView(null)
)
),
Section({name: "IncomingCallView"},
Example({summary: "Default", dashed: "true", style: {width: "280px"}},
IncomingCallView(null)
)
),
Section({name: "ConversationToolbar"},
Example({summary: "Default"},
ConversationToolbar({video: {enabled: true}, audio: {enabled: true}})
),
Example({summary: "Video muted"},
ConversationToolbar({video: {enabled: false}, audio: {enabled: true}})
),
Example({summary: "Audio muted"},
ConversationToolbar({video: {enabled: true}, audio: {enabled: false}})
)
),
Section({name: "ConversationView"},
Example({summary: "Default"},
ConversationView({video: {enabled: true}, audio: {enabled: true}})
)
),
Section({name: "CallUrlExpiredView"},
Example({summary: "Firefox User"},
CallUrlExpiredView({helper: {isFirefox: returnTrue}})
),
Example({summary: "Non-Firefox User"},
CallUrlExpiredView({helper: {isFirefox: returnFalse}})
)
)
)
);
}
});
window.addEventListener("DOMContentLoaded", function() {
React.renderComponent(App(null), document.body);
});
})();

View File

@ -0,0 +1,133 @@
/** @jsx React.DOM */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* jshint newcap:false */
/* global loop:true, React */
(function() {
"use strict";
// 1. Desktop components
// 1.1 Panel
var PanelView = loop.panel.PanelView;
// 1.2. Conversation Window
var IncomingCallView = loop.conversation.IncomingCallView;
// 2. Standalone webapp
var CallUrlExpiredView = loop.webapp.CallUrlExpiredView;
// 3. Shared components
var ConversationToolbar = loop.shared.views.ConversationToolbar;
var ConversationView = loop.shared.views.ConversationView;
// Local helpers
function returnTrue() {
return true;
}
function returnFalse() {
return false;
}
var Example = React.createClass({
render: function() {
var cx = React.addons.classSet;
return (
<div className="example">
<h3>{this.props.summary}</h3>
<div className={cx({comp: true, dashed: this.props.dashed})}
style={this.props.style || {}}>
{this.props.children}
</div>
</div>
);
}
});
var Section = React.createClass({
render: function() {
return (
<section id={this.props.name}>
<h1>{this.props.name}</h1>
{this.props.children}
</section>
);
}
});
var ShowCase = React.createClass({
render: function() {
return (
<div className="showcase">
<header>
<h1>Loop UI Components Showcase</h1>
<nav className="menu">{
React.Children.map(this.props.children, function(section) {
return (
<a className="btn btn-info" href={"#" + section.props.name}>
{section.props.name}
</a>
);
})
}</nav>
</header>
{this.props.children}
</div>
);
}
});
var App = React.createClass({
render: function() {
return (
<ShowCase>
<Section name="PanelView">
<Example summary="332px wide" dashed="true" style={{width: "332px"}}>
<PanelView />
</Example>
</Section>
<Section name="IncomingCallView">
<Example summary="Default" dashed="true" style={{width: "280px"}}>
<IncomingCallView />
</Example>
</Section>
<Section name="ConversationToolbar">
<Example summary="Default">
<ConversationToolbar video={{enabled: true}} audio={{enabled: true}} />
</Example>
<Example summary="Video muted">
<ConversationToolbar video={{enabled: false}} audio={{enabled: true}} />
</Example>
<Example summary="Audio muted">
<ConversationToolbar video={{enabled: true}} audio={{enabled: false}} />
</Example>
</Section>
<Section name="ConversationView">
<Example summary="Default">
<ConversationView video={{enabled: true}} audio={{enabled: true}} />
</Example>
</Section>
<Section name="CallUrlExpiredView">
<Example summary="Firefox User">
<CallUrlExpiredView helper={{isFirefox: returnTrue}} />
</Example>
<Example summary="Non-Firefox User">
<CallUrlExpiredView helper={{isFirefox: returnFalse}} />
</Example>
</Section>
</ShowCase>
);
}
});
window.addEventListener("DOMContentLoaded", function() {
React.renderComponent(<App />, document.body);
});
})();