This commit is contained in:
Sean Li 2015-02-03 16:29:15 -08:00
Родитель 927629b1d8
Коммит 263debf312
13 изменённых файлов: 186 добавлений и 14 удалений

Двоичные данные
images/running 2.png Normal file

Двоичный файл не отображается.

После

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

Двоичные данные
images/running 2@2x.png Normal file

Двоичный файл не отображается.

После

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

Двоичные данные
images/running.png

Двоичный файл не отображается.

До

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

После

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

Двоичные данные
images/running@2x.png

Двоичный файл не отображается.

До

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

После

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

Двоичные данные
images/runningwave.png

Двоичный файл не отображается.

До

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

После

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

Двоичные данные
images/runningwave@2x.png

Двоичный файл не отображается.

До

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

После

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

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

@ -16,7 +16,8 @@
"test:integration": "gulp test --silent --integration",
"all-tests": "npm test && npm run integration-tests",
"release": "gulp run release",
"preinstall": "./deps"
"preinstall": "./deps",
"lint": "jsxhint src/**/*"
},
"licenses": [
{

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

@ -5,6 +5,7 @@ var ContainerModal = require('./ContainerModal.react');
var ContainerStore = require('./ContainerStore');
var ContainerList = require('./ContainerList.react');
var Header = require('./Header.react');
var router = require('./Router');
var Containers = React.createClass({
mixins: [Router.Navigation, Router.State],
@ -61,6 +62,9 @@ var Containers = React.createClass({
});
}
},
handleNewContainer: function () {
this.transitionTo('new');
},
render: function () {
var sidebarHeaderClass = 'sidebar-header';
if (this.state.sidebarOffset) {
@ -76,9 +80,7 @@ var Containers = React.createClass({
<section className={sidebarHeaderClass}>
<h4>Containers</h4>
<div className="create">
<ModalTrigger modal={<ContainerModal/>}>
<span className="btn-new icon icon-add-3"></span>
</ModalTrigger>
<span className="btn-new icon icon-add-3" onClick={this.handleNewContainer}></span>
</div>
</section>
<section className="sidebar-containers" onScroll={this.handleScroll}>

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

@ -14,6 +14,9 @@ if (process.env.NODE_ENV === 'development') {
}
if (!window.location.hash.length || window.location.hash === '#/') {
router.run(function (Handler) {
React.render(<Handler/>, document.body);
});
SetupStore.run(function () {
boot2docker.ip(function (err, ip) {
if (err) { console.log(err); }
@ -21,21 +24,18 @@ if (!window.location.hash.length || window.location.hash === '#/') {
router.transitionTo('containers');
ContainerStore.init(function (err) {
if (err) { console.log(err); }
router.run(function (Handler) {
React.render(<Handler/>, document.body);
});
});
});
});
} else {
router.run(function (Handler) {
React.render(<Handler/>, document.body);
});
boot2docker.ip(function (err, ip) {
if (err) { console.log(err); }
docker.setHost(ip);
ContainerStore.init(function (err) {
if (err) { console.log(err); }
router.run(function (Handler) {
React.render(<Handler/>, document.body);
});
});
});
}

108
src/NewContainer.react.js Normal file
Просмотреть файл

@ -0,0 +1,108 @@
var $ = require('jquery');
var React = require('react/addons');
var RetinaImage = require('react-retina-image');
var ContainerStore = require('./ContainerStore');
var NewContainer = React.createClass({
_searchRequest: null,
getInitialState: function () {
return {
query: '',
results: ContainerStore.recommended(),
loading: false,
tags: {},
active: null,
};
},
componentDidMount: function () {
this.refs.searchInput.getDOMNode().focus();
ContainerStore.on(ContainerStore.CLIENT_RECOMMENDED_EVENT, this.update);
},
update: function () {
if (!this.state.query.length) {
this.setState({
results: ContainerStore.recommended()
});
}
},
search: function (query) {
if (this._searchRequest) {
this._searchRequest.abort();
this._searchRequest = null;
}
if (!query.length) {
return;
}
this.setState({
loading: true
});
var self = this;
this._searchRequest = $.get('https://registry.hub.docker.com/v1/search?q=' + query, function (result) {
self.setState({
query: query,
loading: false
});
self._searchRequest = null;
if (self.isMounted()) {
self.setState(result);
}
});
},
handleChange: function (e) {
var query = e.target.value;
if (query === this.state.query) {
return;
}
clearTimeout(this.timeout);
if (!query.length) {
this.setState({
query: query,
results: ContainerStore.recommended()
});
} else {
var self = this;
this.timeout = setTimeout(function () {
self.search(query);
}, 200);
}
},
render: function () {
var loadingClasses = React.addons.classSet({
hidden: !this.state.loading,
loading: true
});
var magnifierClasses = React.addons.classSet({
hidden: this.state.loading,
icon: true,
'icon-magnifier': true,
'search-icon': true
});
return (
<div className="details">
<div className="detail-panel">
<div className="new-container">
<div className="new-container-header">
<div className="text">
Pick an image to create new container.
</div>
<div className="search">
<div className="search-bar">
<input type="search" ref="searchInput" className="form-control" placeholder="Find an existing image" onChange={this.handleChange}/>
<div className={magnifierClasses}></div>
<RetinaImage className={loadingClasses} src="loading.png"/>
</div>
</div>
</div>
</div>
</div>
</div>
);
}
});
module.exports = NewContainer;

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

@ -3,7 +3,7 @@ var Setup = require('./Setup.react');
var Containers = require('./Containers.react');
var ContainerDetails = require('./ContainerDetails.react');
var Preferences = require('./Preferences.react');
var NoContainers = require('./NoContainers.react');
var NewContainer = require('./NewContainer.react');
var Router = require('react-router');
var Route = Router.Route;
@ -23,7 +23,7 @@ var routes = (
<Route name="containers" handler={Containers}>
<Route name="container" path="/containers/:name" handler={ContainerDetails}/>
<Route name="preferences" path="/preferences" handler={Preferences}/>
<DefaultRoute handler={NoContainers}/>
<DefaultRoute name="new" handler={NewContainer}/>
</Route>
<Route name="setup" handler={Setup}></Route>
<DefaultRoute handler={Setup}/>

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

@ -19,6 +19,66 @@
}
}
.new-container {
padding: 35px 20px 32px 25px;
.new-container-header {
display: flex;
flex: 1;
.text {
flex: 1 auto;
width: 50%;
font-size: 14px;
color: @gray-lighter;
}
.search {
flex: 1 auto;
margin-right: 30px;
.search-bar {
top: -7px;
position: relative;
.loading {
position: absolute;
left: 10px;
top: 7px;
width: 16px;
height: 16px;
-webkit-animation-name: spin;
-webkit-animation-duration: 1.8s;
-webkit-animation-iteration-count: infinite;
-webkit-animation-timing-function: linear;
}
.search-icon {
font-size: 18px;
color: @gray-lighter;
position: absolute;
top: 5px;
left: 10px;
}
input {
border-radius: 20px;
font-size: 12px;
height: 30px;
padding: 4px 8px 4px 35px;
color: @gray-darkest;
margin-bottom: 3px;
border-color: @gray-lightest;
box-shadow: none;
&:focus {
box-shadow: none;
border-color: @gray-lighter;
}
&::-webkit-input-placeholder {
color: #CCC;
font-weight: 400;
}
}
}
}
}
}
.containers {
box-sizing: border-box;
height: 100%;
@ -293,6 +353,7 @@
}
.details {
background-color: #F9F9F9;
margin: 0;
padding: 0;
box-sizing: border-box;

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

@ -1,7 +1,7 @@
@brand-primary: #24B8EB;
@brand-action: #24B8EB;
@brand-positive: #16E568;
@brand-negative: #F47A45;
@brand-positive: #15CC35;
@brand-negative: #FF5F52;
@gray-darkest: #253237;
@gray-darker: #394C51;