diff --git a/app/ContainerModal.react.js b/app/ContainerModal.react.js
index 383ccf0191..8fff6ddde7 100644
--- a/app/ContainerModal.react.js
+++ b/app/ContainerModal.react.js
@@ -1,7 +1,13 @@
var async = require('async');
var $ = require('jquery');
+var assign = require('object-assign');
var React = require('react/addons');
-var Modal = require('react-bootstrap/Modal');
+var Modal = require('react-bootstrap').Modal;
+var OverlayTrigger = require('react-bootstrap');
+var Popover = require('react-bootstrap/Popover');
+var SplitButton = require('react-bootstrap/SplitButton');
+var MenuItem = require('react-bootstrap/MenuItem');
+
var RetinaImage = require('react-retina-image');
var ContainerStore = require('./ContainerStore');
@@ -12,6 +18,8 @@ var ContainerModal = React.createClass({
query: '',
results: ContainerStore.recommended(),
loading: false,
+ tags: {},
+ active: null,
};
},
componentDidMount: function () {
@@ -67,11 +75,52 @@ var ContainerModal = React.createClass({
}, 200);
}
},
- handleClick: function (event) {
- var name = event.target.getAttribute('name');
- var self = this;
+ handleClick: function (name, event) {
ContainerStore.create(name, 'latest', function (err, containerName) {
- self.props.onRequestHide();
+ this.props.onRequestHide();
+ }.bind(this));
+ },
+ handleTagClick: function (tag, name, event) {
+ ContainerStore.create(name, tag, function (err, containerName) {
+ this.props.onRequestHide();
+ }.bind(this));
+ },
+ handleDropdownClick: function (name, event) {
+ this.setState({
+ active: name
+ });
+ if (this.state.tags[name]) {
+ return;
+ }
+ $.get('https://registry.hub.docker.com/v1/repositories/' + name + '/tags', function (result) {
+ var res = {};
+ res[name] = result;
+ console.log(assign(this.state.tags, res));
+ this.setState({
+ tags: assign(this.state.tags, res)
+ });
+ }.bind(this));
+ },
+ handleModalClick: function (event) {
+ if (!this.state.active) {
+ return;
+ }
+ if (!$('.popover').is(event.target)) {
+ this.setState({
+ active: null
+ });
+ }
+ },
+ componentDidUpdate: function () {
+ if (!this.state.active) {
+ return;
+ }
+ var $dropdown = $(this.getDOMNode()).find('[data-name="' + this.state.active + '"]');
+ var $popover = $(this.getDOMNode()).find('.popover');
+
+ $popover.offset({
+ top: $dropdown.offset().top + 32,
+ left: $dropdown.offset().left - $popover.width() / 2 + 11
});
},
render: function () {
@@ -87,6 +136,7 @@ var ContainerModal = React.createClass({
} else {
name =
{r.name};
}
+
return (
@@ -100,8 +150,11 @@ var ContainerModal = React.createClass({
-
Create
-
+
+
@@ -137,26 +190,50 @@ var ContainerModal = React.createClass({
'search-icon': true
});
+ var question = (
+
+ An image is a template for a container.}>
+ What's an image?
+
+
+ );
+
+ var tagData = self.state.tags[this.state.active];
+ if (tagData) {
+ var list = tagData.map(function (t) {
+ return
{t.name};
+ });
+ tags = (
+
+ );
+ } else {
+ tags =
;
+ }
+
+ var popoverClasses = React.addons.classSet({
+ popover: true,
+ hidden: !this.state.active
+ });
+
return (
-
+
-
+
+ {tags}
+
);
diff --git a/app/ContainerStore.js b/app/ContainerStore.js
index 05a9c84998..c8701934cd 100644
--- a/app/ContainerStore.js
+++ b/app/ContainerStore.js
@@ -269,12 +269,13 @@ var ContainerStore = assign(EventEmitter.prototype, {
async.map(recommended, function (repository, callback) {
$.get('https://registry.hub.docker.com/v1/search?q=' + repository, function (data) {
var results = data.results;
+ console.log(repository, data);
callback(null, _.find(results, function (r) {
return r.name === repository;
}));
});
}, function (err, results) {
- _recommended = results;
+ _recommended = results.filter(function(r) { return !!r; });
callback();
});
},
diff --git a/app/Containers.react.js b/app/Containers.react.js
index 30a640273b..45ab3f9fd5 100644
--- a/app/Containers.react.js
+++ b/app/Containers.react.js
@@ -1,6 +1,5 @@
var React = require('react/addons');
var Router = require('react-router');
-var Modal = require('react-bootstrap/Modal');
var RetinaImage = require('react-retina-image');
var ModalTrigger = require('react-bootstrap/ModalTrigger');
var ContainerModal = require('./ContainerModal.react');
diff --git a/app/styles/main.less b/app/styles/container-modal.less
similarity index 72%
rename from app/styles/main.less
rename to app/styles/container-modal.less
index 6c5055a63a..6fd190d0e7 100644
--- a/app/styles/main.less
+++ b/app/styles/container-modal.less
@@ -1,60 +1,18 @@
-@import "bootstrap/bootstrap.less";
-@import "clearsans.less";
-@import "theme.less";
-@import "icons.less";
-@import "retina.less";
-@import "setup.less";
-@import "radial.less";
-@import "header.less";
-@import "containers.less";
-
-html, body {
- height: 100%;
- width: 100%;
- overflow: hidden;
- -webkit-font-smoothing: antialiased;
- -webkit-user-select: none;
- font-family: 'Clear Sans', sans-serif;
-
- cursor: default;
- img {
- pointer-events: none;
- }
-}
-
-::-webkit-scrollbar {
- width: 13px;
-}
-
-::-webkit-scrollbar-track {
- margin: 3px;
- -webkit-border-radius: 5px;
- border-radius: 5px;
- background: none;
-}
-
-::-webkit-scrollbar-thumb {
- border: 3px solid rgba(0, 0, 0, 0);
- background-clip: padding-box;
- width: 7px;
- border-radius: 8px;
- background-color: rgba(0,0,0,0.2);
-}
-
.create-modal {
@modal-padding: 32px;
@search-width: 372px;
@custom-width: 270px;
.modal-dialog {
- margin-top: 8%;
+ margin-top: 80px;
width: calc(@modal-padding + @search-width + 2 * @modal-padding + @custom-width);
}
.modal-content {
//box-shadow: 0 3px 15px rgba(0, 0, 0, 0.2);
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.10);
border: none; //1px solid #ccc;
- height: 610px;
+ height: 650px;
display: flex;
+
}
.modal-body {
flex: 1 auto;
@@ -68,18 +26,47 @@ html, body {
min-width: 270px;
}
+ .popover {
+ width: 180px;
+ text-align: center;
+
+ .popover-content {
+ max-height: 300px;
+ padding: 0;
+ overflow: auto;
+ }
+ ul {
+ padding: 0;
+ list-style: none;
+ margin: 0;
+ li {
+ padding: 8px 0;
+ border-bottom: 1px solid #eee;
+
+ &:hover {
+ color: white;
+ background: @brand-primary;
+ }
+ }
+ }
+ .tags-loading {
+ text-align: center;
+ margin: 14px auto;
+ text-align: center;
+ -webkit-animation-name: spin;
+ -webkit-animation-duration: 1.8s;
+ -webkit-animation-iteration-count: infinite;
+ -webkit-animation-timing-function: linear;
+ }
+ }
+
section.search {
min-width: 404px;
padding-right: 32px;
border-right: 1px solid #eee;
.question {
- a {
- color: @gray-lightest;
- &:hover {
- color: darken(@gray-lightest, 10%);
- }
- }
+ color: @gray-lightest;
font-size: 10px;
text-align: right;
}
@@ -128,6 +115,7 @@ html, body {
.results {
overflow: auto;
+ padding-bottom: 80px;
.no-results {
text-align: center;
@@ -200,6 +188,20 @@ html, body {
top: 5px;
text-align: right;
flex: 1 auto;
+ ul {
+ text-align: center;
+ ul {
+ overflow: auto;
+ max-height: 300px;
+ }
+ }
+
+
+ .icon {
+ position: relative;
+ top: 2px;
+ font-size: 11px;
+ }
}
}
}
@@ -213,40 +215,3 @@ html, body {
opacity: 1;
height: 100%;
}
-
-@-webkit-keyframes spin {
- from {
- -webkit-transform: rotate(0deg);
- }
- to {
- -webkit-transform: rotate(360deg);
- }
-}
-
-@-webkit-keyframes translatewave {
- from {
- -webkit-transform: translateX(0px);
- }
- to {
- -webkit-transform: translateX(20px);
- }
-}
-
-@-webkit-keyframes translatedownload {
- 0% {
- -webkit-transform: translateY(6px);
- opacity: 0;
- }
- 25% {
- opacity: 1;
- -webkit-transform: translateY(6px);
- }
- 50% {
- opacity: 1;
- -webkit-transform: translateY(20px);
- }
- 100% {
- opacity: 1;
- -webkit-transform: translateY(20px);
- }
-}
diff --git a/app/styles/theme.less b/app/styles/theme.less
index fd87da3215..ac9d02e910 100644
--- a/app/styles/theme.less
+++ b/app/styles/theme.less
@@ -39,6 +39,7 @@ h4 {
color: darken(@btn-color, 10%);
cursor: default;
box-shadow: none;
+ background: none;
}
&:active {