Made default page working.
После Ширина: | Высота: | Размер: 554 B |
После Ширина: | Высота: | Размер: 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}>
|
||||
|
|
12
src/Main.js
|
@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|