Made default page working.
After Width: | Height: | Size: 554 B |
After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 527 B After Width: | Height: | Size: 555 B |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 347 B After Width: | Height: | Size: 356 B |
Before Width: | Height: | Size: 638 B After Width: | Height: | Size: 657 B |
|
@ -16,7 +16,8 @@
|
||||||
"test:integration": "gulp test --silent --integration",
|
"test:integration": "gulp test --silent --integration",
|
||||||
"all-tests": "npm test && npm run integration-tests",
|
"all-tests": "npm test && npm run integration-tests",
|
||||||
"release": "gulp run release",
|
"release": "gulp run release",
|
||||||
"preinstall": "./deps"
|
"preinstall": "./deps",
|
||||||
|
"lint": "jsxhint src/**/*"
|
||||||
},
|
},
|
||||||
"licenses": [
|
"licenses": [
|
||||||
{
|
{
|
||||||
|
|
|
@ -5,6 +5,7 @@ var ContainerModal = require('./ContainerModal.react');
|
||||||
var ContainerStore = require('./ContainerStore');
|
var ContainerStore = require('./ContainerStore');
|
||||||
var ContainerList = require('./ContainerList.react');
|
var ContainerList = require('./ContainerList.react');
|
||||||
var Header = require('./Header.react');
|
var Header = require('./Header.react');
|
||||||
|
var router = require('./Router');
|
||||||
|
|
||||||
var Containers = React.createClass({
|
var Containers = React.createClass({
|
||||||
mixins: [Router.Navigation, Router.State],
|
mixins: [Router.Navigation, Router.State],
|
||||||
|
@ -61,6 +62,9 @@ var Containers = React.createClass({
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
handleNewContainer: function () {
|
||||||
|
this.transitionTo('new');
|
||||||
|
},
|
||||||
render: function () {
|
render: function () {
|
||||||
var sidebarHeaderClass = 'sidebar-header';
|
var sidebarHeaderClass = 'sidebar-header';
|
||||||
if (this.state.sidebarOffset) {
|
if (this.state.sidebarOffset) {
|
||||||
|
@ -76,9 +80,7 @@ var Containers = React.createClass({
|
||||||
<section className={sidebarHeaderClass}>
|
<section className={sidebarHeaderClass}>
|
||||||
<h4>Containers</h4>
|
<h4>Containers</h4>
|
||||||
<div className="create">
|
<div className="create">
|
||||||
<ModalTrigger modal={<ContainerModal/>}>
|
<span className="btn-new icon icon-add-3" onClick={this.handleNewContainer}></span>
|
||||||
<span className="btn-new icon icon-add-3"></span>
|
|
||||||
</ModalTrigger>
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<section className="sidebar-containers" onScroll={this.handleScroll}>
|
<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 === '#/') {
|
if (!window.location.hash.length || window.location.hash === '#/') {
|
||||||
|
router.run(function (Handler) {
|
||||||
|
React.render(<Handler/>, document.body);
|
||||||
|
});
|
||||||
SetupStore.run(function () {
|
SetupStore.run(function () {
|
||||||
boot2docker.ip(function (err, ip) {
|
boot2docker.ip(function (err, ip) {
|
||||||
if (err) { console.log(err); }
|
if (err) { console.log(err); }
|
||||||
|
@ -21,21 +24,18 @@ if (!window.location.hash.length || window.location.hash === '#/') {
|
||||||
router.transitionTo('containers');
|
router.transitionTo('containers');
|
||||||
ContainerStore.init(function (err) {
|
ContainerStore.init(function (err) {
|
||||||
if (err) { console.log(err); }
|
if (err) { console.log(err); }
|
||||||
router.run(function (Handler) {
|
|
||||||
React.render(<Handler/>, document.body);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
router.run(function (Handler) {
|
||||||
|
React.render(<Handler/>, document.body);
|
||||||
|
});
|
||||||
boot2docker.ip(function (err, ip) {
|
boot2docker.ip(function (err, ip) {
|
||||||
if (err) { console.log(err); }
|
if (err) { console.log(err); }
|
||||||
docker.setHost(ip);
|
docker.setHost(ip);
|
||||||
ContainerStore.init(function (err) {
|
ContainerStore.init(function (err) {
|
||||||
if (err) { console.log(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 Containers = require('./Containers.react');
|
||||||
var ContainerDetails = require('./ContainerDetails.react');
|
var ContainerDetails = require('./ContainerDetails.react');
|
||||||
var Preferences = require('./Preferences.react');
|
var Preferences = require('./Preferences.react');
|
||||||
var NoContainers = require('./NoContainers.react');
|
var NewContainer = require('./NewContainer.react');
|
||||||
var Router = require('react-router');
|
var Router = require('react-router');
|
||||||
|
|
||||||
var Route = Router.Route;
|
var Route = Router.Route;
|
||||||
|
@ -23,7 +23,7 @@ var routes = (
|
||||||
<Route name="containers" handler={Containers}>
|
<Route name="containers" handler={Containers}>
|
||||||
<Route name="container" path="/containers/:name" handler={ContainerDetails}/>
|
<Route name="container" path="/containers/:name" handler={ContainerDetails}/>
|
||||||
<Route name="preferences" path="/preferences" handler={Preferences}/>
|
<Route name="preferences" path="/preferences" handler={Preferences}/>
|
||||||
<DefaultRoute handler={NoContainers}/>
|
<DefaultRoute name="new" handler={NewContainer}/>
|
||||||
</Route>
|
</Route>
|
||||||
<Route name="setup" handler={Setup}></Route>
|
<Route name="setup" handler={Setup}></Route>
|
||||||
<DefaultRoute handler={Setup}/>
|
<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 {
|
.containers {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
@ -293,6 +353,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.details {
|
.details {
|
||||||
|
background-color: #F9F9F9;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
@brand-primary: #24B8EB;
|
@brand-primary: #24B8EB;
|
||||||
@brand-action: #24B8EB;
|
@brand-action: #24B8EB;
|
||||||
@brand-positive: #16E568;
|
@brand-positive: #15CC35;
|
||||||
@brand-negative: #F47A45;
|
@brand-negative: #FF5F52;
|
||||||
|
|
||||||
@gray-darkest: #253237;
|
@gray-darkest: #253237;
|
||||||
@gray-darker: #394C51;
|
@gray-darker: #394C51;
|
||||||
|
|