diff --git a/images/running 2.png b/images/running 2.png new file mode 100644 index 0000000000..4a81d6da97 Binary files /dev/null and b/images/running 2.png differ diff --git a/images/running 2@2x.png b/images/running 2@2x.png new file mode 100644 index 0000000000..f72445227e Binary files /dev/null and b/images/running 2@2x.png differ diff --git a/images/running.png b/images/running.png index ea129def4f..f54d02e9d2 100644 Binary files a/images/running.png and b/images/running.png differ diff --git a/images/running@2x.png b/images/running@2x.png index b2777164bf..43517a2c30 100644 Binary files a/images/running@2x.png and b/images/running@2x.png differ diff --git a/images/runningwave.png b/images/runningwave.png index d65fc18e13..eb7bfc8e2e 100644 Binary files a/images/runningwave.png and b/images/runningwave.png differ diff --git a/images/runningwave@2x.png b/images/runningwave@2x.png index bb2a5ef49f..6293314b0b 100644 Binary files a/images/runningwave@2x.png and b/images/runningwave@2x.png differ diff --git a/package.json b/package.json index 9371f56922..e91347baf7 100644 --- a/package.json +++ b/package.json @@ -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": [ { diff --git a/src/Containers.react.js b/src/Containers.react.js index 3f9cfbff32..aeafab212e 100644 --- a/src/Containers.react.js +++ b/src/Containers.react.js @@ -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({ Containers - }> - - + diff --git a/src/Main.js b/src/Main.js index 4582a2235f..cbc6c57068 100644 --- a/src/Main.js +++ b/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(, 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(, document.body); - }); }); }); }); } else { + router.run(function (Handler) { + React.render(, 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(, document.body); - }); }); }); } diff --git a/src/NewContainer.react.js b/src/NewContainer.react.js new file mode 100644 index 0000000000..5f1162bae5 --- /dev/null +++ b/src/NewContainer.react.js @@ -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 ( + + + + + + Pick an image to create new container. + + + + + + + + + + + + + ); + } +}); + +module.exports = NewContainer; diff --git a/src/Routes.js b/src/Routes.js index 92b9df7d3a..a577ed605b 100644 --- a/src/Routes.js +++ b/src/Routes.js @@ -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 = ( - + diff --git a/styles/containers.less b/styles/containers.less index 5bf7647653..6651ced1bd 100644 --- a/styles/containers.less +++ b/styles/containers.less @@ -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; diff --git a/styles/variables.less b/styles/variables.less index 3d8a1b7f92..ba3833fa96 100644 --- a/styles/variables.less +++ b/styles/variables.less @@ -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;