From 9709e5fec4aba193722049d56ff0246276c94c5a Mon Sep 17 00:00:00 2001 From: Sean Li Date: Wed, 4 Feb 2015 12:44:48 -0800 Subject: [PATCH] Grid implementation. --- images/container-white.png | Bin 0 -> 128 bytes images/container-white@2x.png | Bin 0 -> 193 bytes images/container.png | Bin 0 -> 126 bytes images/container@2x.png | Bin 0 -> 201 bytes package.json | 2 +- src/ContainerList.react.js | 2 + src/ContainerListNewItem.react.js | 43 +++++++++++ src/ContainerModal.react.js | 8 +- src/ContainerStore.js | 7 +- src/Containers.react.js | 10 +-- src/NewContainer.react.js | 90 +++++++++++++++++++++++ src/Setup.react.js | 5 -- styles/containers.less | 117 +++++++++++++++++++++++++++++- 13 files changed, 264 insertions(+), 20 deletions(-) create mode 100644 images/container-white.png create mode 100644 images/container-white@2x.png create mode 100644 images/container.png create mode 100644 images/container@2x.png create mode 100644 src/ContainerListNewItem.react.js diff --git a/images/container-white.png b/images/container-white.png new file mode 100644 index 0000000000000000000000000000000000000000..94fc0e6bad1efe20316fe6f079a47084c96519bf GIT binary patch literal 128 zcmeAS@N?(olHy`uVBq!ia0vp^A|TAc1|)ksWqE-VV{wqX6T`Z5GB1G~D^C~45DUTN z1PRtDZ48g=6&73(Ncrn?LT1%Q$8{`>Ga3ctDr;8lF1X?lv1NjP{{aITrO5t27P4Jd ZjIYi0pHH>0T?RCT!PC{xWt~$(69D(ICQbkV literal 0 HcmV?d00001 diff --git a/images/container-white@2x.png b/images/container-white@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..dc5fde9ef5ae0562e0705e5aa5aca4971d4afcd7 GIT binary patch literal 193 zcmeAS@N?(olHy`uVBq!ia0vp^8X(NU1|)m_?Z^dEjKx9jP7LeL$-D$|sy$sCLn1ie zUf;-dK!K+v@$v8do3APsl + {containers} ); diff --git a/src/ContainerListNewItem.react.js b/src/ContainerListNewItem.react.js new file mode 100644 index 0000000000..3260aceb2f --- /dev/null +++ b/src/ContainerListNewItem.react.js @@ -0,0 +1,43 @@ +var $ = require('jquery'); +var React = require('react/addons'); +var Router = require('react-router'); + +var ContainerListNewItem = React.createClass({ + handleItemMouseEnter: function () { + var $action = $(this.getDOMNode()).find('.action'); + $action.show(); + }, + handleItemMouseLeave: function () { + var $action = $(this.getDOMNode()).find('.action'); + $action.hide(); + }, + handleDelete: function () { + $(this.getDOMNode()).fadeOut(); + }, + render: function () { + var self = this; + var action; + if (this.props.containers.length > 0) { + action = ( +
+ +
+ ); + } + return ( + +
  • +
    +
    +
    + New Container +
    +
    + {action} +
  • +
    + ); + } +}); + +module.exports = ContainerListNewItem; diff --git a/src/ContainerModal.react.js b/src/ContainerModal.react.js index 0f6a13bda4..a78ef0edd0 100644 --- a/src/ContainerModal.react.js +++ b/src/ContainerModal.react.js @@ -77,17 +77,17 @@ var ContainerModal = React.createClass({ }, 200); } }, - handleClick: function (name, event) { + handleClick: function (name) { this.props.onRequestHide(); - ContainerStore.create(name, 'latest', function (err, containerName) { + ContainerStore.create(name, 'latest', function (err) { if (err) { throw err; } }.bind(this)); }, - handleTagClick: function (tag, name, event) { + handleTagClick: function (tag, name) { this.props.onRequestHide(); - ContainerStore.create(name, tag, function (err, containerName) {}); + ContainerStore.create(name, tag, function () {}); }, handleDropdownClick: function (name) { this.setState({ diff --git a/src/ContainerStore.js b/src/ContainerStore.js index 32773181f4..7e3ad1020a 100644 --- a/src/ContainerStore.js +++ b/src/ContainerStore.js @@ -118,11 +118,11 @@ var ContainerStore = assign(EventEmitter.prototype, { if (containerData.Config && containerData.Config.Image) { containerData.Image = containerData.Config.Image; } - existing.kill(function (err, data) { + existing.kill(function (err) { if (err) { console.log(err); } - existing.remove(function (err, data) { + existing.remove(function (err) { if (err) { console.log(err); } @@ -386,7 +386,6 @@ var ContainerStore = assign(EventEmitter.prototype, { var self = this; var imageName = repository + ':' + tag; var containerName = this._generateName(repository); - var image = docker.client().getImage(imageName); // Pull image self._createPlaceholderContainer(imageName, containerName, function (err, container) { if (err) { @@ -398,7 +397,7 @@ var ContainerStore = assign(EventEmitter.prototype, { _muted[containerName] = true; _progress[containerName] = 0; self._pullImage(repository, tag, function () { - self._createContainer(containerName, {Image: imageName}, function (err, container) { + self._createContainer(containerName, {Image: imageName}, function () { delete _progress[containerName]; _muted[containerName] = false; self.emit(self.CLIENT_CONTAINER_EVENT, containerName); diff --git a/src/Containers.react.js b/src/Containers.react.js index aeafab212e..81070e7ba2 100644 --- a/src/Containers.react.js +++ b/src/Containers.react.js @@ -1,11 +1,9 @@ +var $ = require('jquery'); var React = require('react/addons'); var Router = require('react-router'); -var ModalTrigger = require('react-bootstrap/ModalTrigger'); -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], @@ -13,7 +11,7 @@ var Containers = React.createClass({ return { sidebarOffset: 0, containers: ContainerStore.containers(), - sorted: ContainerStore.sorted(), + sorted: ContainerStore.sorted() }; }, componentDidMount: function () { @@ -63,6 +61,8 @@ var Containers = React.createClass({ } }, handleNewContainer: function () { + console.log($(this.getDOMNode()).find('.new-container-item')); + $(this.getDOMNode()).find('.new-container-item').parent().fadeIn(); this.transitionTo('new'); }, render: function () { @@ -84,7 +84,7 @@ var Containers = React.createClass({
    - +
    diff --git a/src/NewContainer.react.js b/src/NewContainer.react.js index 5f1162bae5..33e97d34a2 100644 --- a/src/NewContainer.react.js +++ b/src/NewContainer.react.js @@ -2,6 +2,7 @@ var $ = require('jquery'); var React = require('react/addons'); var RetinaImage = require('react-retina-image'); var ContainerStore = require('./ContainerStore'); +var assign = require('object-assign'); var NewContainer = React.createClass({ _searchRequest: null, @@ -71,7 +72,92 @@ var NewContainer = React.createClass({ }, 200); } }, + handleClick: function (name) { + ContainerStore.create(name, 'latest', function (err) { + if (err) { + throw err; + } + }.bind(this)); + }, + handleDropdownClick: function (name) { + 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)); + }, render: function () { + var self = this; + var title = this.state.query ? 'Results' : 'Recommended'; + var data = this.state.results.slice(0, 7); + + var results; + if (data.length) { + var items = data.map(function (r) { + var name; + if (r.is_official) { + name = {r.name}; + } else { + name = {r.name}; + } + var description; + if (r.description) { + description = r.description; + } else { + description = "No description."; + } + return ( +
    +
    +
    +
    +
    + {name} +
    +
    + {description} +
    +
    +
    + + {r.star_count} +
    +
    + + latest +
    +
    + Create +
    +
    +
    +
    + ); + }); + + results = ( +
    + {items} +
    + ); + } else { + results = ( +
    +

    + No Results +

    +
    + ); + } var loadingClasses = React.addons.classSet({ hidden: !this.state.loading, loading: true @@ -98,6 +184,10 @@ var NewContainer = React.createClass({ +
    +

    {title}

    + {results} +
    diff --git a/src/Setup.react.js b/src/Setup.react.js index 8df59eb28e..ef3bd7160f 100644 --- a/src/Setup.react.js +++ b/src/Setup.react.js @@ -1,11 +1,6 @@ var React = require('react/addons'); var Router = require('react-router'); var Radial = require('./Radial.react.js'); -var async = require('async'); -var assign = require('object-assign'); -var fs = require('fs'); -var path = require('path'); -var virtualbox = require('./Virtualbox'); var SetupStore = require('./SetupStore'); var RetinaImage = require('react-retina-image'); diff --git a/styles/containers.less b/styles/containers.less index 1eb03f3787..57fcd49f6b 100644 --- a/styles/containers.less +++ b/styles/containers.less @@ -19,9 +19,109 @@ } } +.result-grid { + display: flex; + flex-flow: row wrap; + justify-content: flex-start; + margin-top: 20px; + overflow: auto; + .image-item { + display: flex; + .logo { + min-width: 90px; + height: 100%; + background-color: @brand-action; + } + .card { + padding: 10px 20px 10px 20px; + .name { + font-size: 18px; + color: @gray-darkest; + margin-bottom: 5px; + img { + margin-right: 7px; + position: relative; + top: -1px; + } + } + .description { + font-size: 12px; + color: @gray-normal; + height: 70px; + text-overflow: ellipsis; + overflow: hidden; + -webkit-box-orient: vertical; + -webkit-line-clamp: 4; + display: -webkit-box; + word-wrap: break-word; + } + .actions { + width: 190px; + padding-top: 25px; + .stars { + height: 15px; + font-size: 10px; + color: @gray-darker; + border-right: 1px solid @gray-lightest; + padding-right: 10px; + .icon { + position: relative; + font-size: 16px; + margin-right: 3px; + top: -1px; + color: @gray-darkest; + } + .text { + position: relative; + top: -6px; + } + } + .tags { + height: 15px; + font-size: 10px; + color: @gray-darker; + padding-left: 10px; + .icon { + position: relative; + font-size: 11px; + margin-right: 5px; + top: 2px; + color: @gray-darkest; + } + .text { + position: relative; + top: 0px; + } + } + .action { + flex: 1 auto; + .btn { + text-align: right; + position: relative; + float: right; + top: -7px; + } + } + display: flex; + flex-direaction: row; + position: relative; + bottom: 0px; + } + } + width: 320px; + height: 170px; + border-radius: 4px; + border: 1px solid @gray-lightest; + background-color: white; + margin-right: 20px; + margin-bottom: 20px; + } +} + .new-container { padding: 35px 20px 32px 25px; .new-container-header { + margin-bottom: 20px; display: flex; flex: 1; .text { @@ -160,6 +260,13 @@ display: flex; flex-direction: column; + .new-container-item { + .info { + position: relative; + top: 9px; + } + } + a { color: inherit; flex-shrink: 0; @@ -168,6 +275,7 @@ &.active { background: @brand-primary; li { + height: 57px; border-bottom: none; background-image: linear-gradient(-180deg, #24B8EB 4%, #24A3EB 100%); .name { @@ -187,6 +295,9 @@ color: white; } } + .state-new { + .at2x('container-white.png', 20px, 20px); + } .state-running { .at2x('running-white.png', 20px, 20px); @@ -197,7 +308,6 @@ .state-stopped { .at2x('stopped-white.png', 20px, 20px); } - .state-downloading { .at2x('downloading-white.png', 20px, 20px); @@ -223,6 +333,7 @@ display: flex; flex-direction: row; + height: 57px; .info { @@ -283,6 +394,10 @@ .at2x('paused.png', 20px, 20px); } + .state-new { + .at2x('container.png', 20px, 20px); + } + .state-downloading { .at2x('downloading.png', 20px, 20px); overflow: hidden;