-
Logs
-
- {this.props.logs}
-
+ } else {
+ if (this.state.defaultPort) {
+ body = (
+
+
-
-
Edit Files
-
- {folders}
+
+ );
+ } else {
+ var right;
+ if (_.keys(this.state.ports) > 0) {
+ right = (
+
+
+
+
+ );
+ } else {
+ right = (
+
+
+
+ );
+ }
+ body = (
+
+
+
+
-
Change Folders
+ {right}
-
-
- );
+ );
+ }
+ }
+ return body;
}
});
diff --git a/src/ContainerHomeFolders.react.js b/src/ContainerHomeFolders.react.js
new file mode 100644
index 0000000000..3f4d45f5ba
--- /dev/null
+++ b/src/ContainerHomeFolders.react.js
@@ -0,0 +1,48 @@
+var _ = require('underscore');
+var React = require('react/addons');
+var RetinaImage = require('react-retina-image');
+var path = require('path');
+var exec = require('exec');
+var Router = require('react-router');
+
+var ContainerHomeFolder = React.createClass({
+ mixins: [Router.State, Router.Navigation],
+ handleClickFolder: function (path) {
+ exec(['open', path], function (err) {
+ if (err) { throw err; }
+ });
+ },
+ handleClickChangeFolders: function () {
+ this.transitionTo('containerSettingsVolumes', {name: this.getParams().name});
+ },
+ render: function () {
+ var folders;
+ if (this.props.container) {
+ var self = this;
+ folders = _.map(self.props.container.Volumes, function (val, key) {
+ var firstFolder = key.split(path.sep)[1];
+ if (!val || val.indexOf(process.env.HOME) === -1) {
+ return;
+ } else {
+ return (
+
+ );
+ }
+ });
+ }
+ return (
+
+
Edit Files
+
+ {folders}
+
+
Change Folders
+
+ );
+ }
+});
+
+module.exports = ContainerHomeFolder;
diff --git a/src/ContainerHomeLogs.react.js b/src/ContainerHomeLogs.react.js
new file mode 100644
index 0000000000..2e66352ad1
--- /dev/null
+++ b/src/ContainerHomeLogs.react.js
@@ -0,0 +1,64 @@
+var $ = require('jquery');
+var React = require('react/addons');
+var ContainerStore = require('./ContainerStore');
+var Router = require('react-router');
+
+var ContainerHomeLogs = React.createClass({
+ mixins: [Router.State, Router.Navigation],
+ getInitialState: function () {
+ return {
+ logs: []
+ };
+ },
+ componentWillReceiveProps: function () {
+ this.init();
+ },
+ componentDidMount: function() {
+ this.init();
+ ContainerStore.on(ContainerStore.SERVER_LOGS_EVENT, this.updateLogs);
+ },
+ componentWillUnmount: function() {
+ ContainerStore.removeListener(ContainerStore.SERVER_LOGS_EVENT, this.updateLogs);
+ },
+ componentDidUpdate: function () {
+ // Scroll logs to bottom
+ var parent = $('.mini-logs');
+ if (parent.length) {
+ if (parent.scrollTop() >= this._oldHeight) {
+ parent.stop();
+ parent.scrollTop(parent[0].scrollHeight - parent.height());
+ }
+ this._oldHeight = parent[0].scrollHeight - parent.height();
+ }
+ },
+ init: function () {
+ this.updateLogs();
+ },
+ updateLogs: function (name) {
+ if (name && name !== this.getParams().name) {
+ return;
+ }
+ this.setState({
+ logs: ContainerStore.logs(this.getParams().name)
+ });
+ },
+ handleClickLogs: function () {
+ this.transitionTo('containerLogs', {name: this.getParams().name});
+ },
+ render: function () {
+ var logs = this.state.logs.map(function (l, i) {
+ return
;
+ });
+ return (
+
+ );
+ }
+});
+
+module.exports = ContainerHomeLogs;
diff --git a/src/ContainerHomePreview.react.js b/src/ContainerHomePreview.react.js
new file mode 100644
index 0000000000..dfb12d584e
--- /dev/null
+++ b/src/ContainerHomePreview.react.js
@@ -0,0 +1,97 @@
+var _ = require('underscore');
+var $ = require('jquery');
+var React = require('react/addons');
+var exec = require('exec');
+var ContainerStore = require('./ContainerStore');
+var ContainerUtil = require('./ContainerUtil');
+var Router = require('react-router');
+
+var ContainerHomePreview = React.createClass({
+ mixins: [Router.State, Router.Navigation],
+ getInitialState: function () {
+ return {
+ ports: {},
+ defaultPort: null
+ };
+ },
+ componentWillReceiveProps: function () {
+ this.init();
+ },
+ componentDidMount: function() {
+ this.init();
+ this.timer = setInterval(this.tick, 2000);
+ },
+ tick: function () {
+ if (document.getElementById('web-preview-frame')) {
+ var frameContent = document.getElementById('web-preview-frame').contentDocument;
+ var $body = $(frameContent.body);
+ if ($body.is(':empty')) {
+ document.getElementById('web-preview-frame').contentDocument.location.reload(true);
+ }
+ }
+ },
+ componentWillUnmount: function() {
+ clearInterval(this.timer);
+ },
+ init: function () {
+ var container = ContainerStore.container(this.getParams().name);
+ if (!container) {
+ return;
+ }
+ var ports = ContainerUtil.ports(container);
+ var webPorts = ['80', '8000', '8080', '3000', '5000', '2368'];
+ this.setState({
+ ports: ports,
+ defaultPort: _.find(_.keys(ports), function (port) {
+ return webPorts.indexOf(port) !== -1;
+ })
+ });
+ },
+ handleClickPreview: function () {
+ if (this.state.defaultPort) {
+ exec(['open', this.state.ports[this.state.defaultPort].url], function (err) {
+ if (err) { throw err; }
+ });
+ }
+ },
+ handleClickNotShowingCorrectly: function () {
+ this.transitionTo('containerSettingsPorts', {name: this.getParams().name});
+ },
+ render: function () {
+ var preview;
+ if (this.state.defaultPort) {
+ preview = (
+
+
Web Preview
+
+
Not showing correctly?
+
+ );
+ } else {
+ var ports = _.map(_.pairs(this.state.ports), function (pair) {
+ var key = pair[0];
+ var val = pair[1];
+ return (
+
+ {val.display}
+
+ );
+ });
+ preview = (
+
+
IP & Ports
+
+
You can access this container from the outside using the following IP & Port(s):
+ {ports}
+
+
+ );
+ }
+ return preview;
+ }
+});
+
+module.exports = ContainerHomePreview;
diff --git a/src/ContainerListItem.react.js b/src/ContainerListItem.react.js
index 175def45c8..bf69e31ccd 100644
--- a/src/ContainerListItem.react.js
+++ b/src/ContainerListItem.react.js
@@ -66,7 +66,7 @@ var ContainerListItem = React.createClass({
}
return (
-
+
{state}
diff --git a/src/ContainerListNewItem.react.js b/src/ContainerListNewItem.react.js
index 3260aceb2f..0fb41752de 100644
--- a/src/ContainerListNewItem.react.js
+++ b/src/ContainerListNewItem.react.js
@@ -3,6 +3,7 @@ var React = require('react/addons');
var Router = require('react-router');
var ContainerListNewItem = React.createClass({
+ mixins: [Router.State, Router.Navigation],
handleItemMouseEnter: function () {
var $action = $(this.getDOMNode()).find('.action');
$action.show();
@@ -13,6 +14,7 @@ var ContainerListNewItem = React.createClass({
},
handleDelete: function () {
$(this.getDOMNode()).fadeOut();
+ this.transitionTo('containers');
},
render: function () {
var self = this;
diff --git a/src/ContainerLogs.react.js b/src/ContainerLogs.react.js
new file mode 100644
index 0000000000..e95ccd9781
--- /dev/null
+++ b/src/ContainerLogs.react.js
@@ -0,0 +1,57 @@
+var $ = require('jquery');
+var React = require('react/addons');
+var ContainerStore = require('./ContainerStore');
+var Router = require('react-router');
+
+var ContainerLogs = React.createClass({
+ mixins: [Router.State],
+ getInitialState: function () {
+ return {
+ logs: []
+ };
+ },
+ componentWillReceiveProps: function () {
+ this.init();
+ },
+ componentDidMount: function() {
+ this.init();
+ ContainerStore.on(ContainerStore.SERVER_LOGS_EVENT, this.updateLogs);
+ },
+ componentWillUnmount: function() {
+ ContainerStore.removeListener(ContainerStore.SERVER_LOGS_EVENT, this.updateLogs);
+ },
+ componentDidUpdate: function () {
+ // Scroll logs to bottom
+ var parent = $('.details-logs');
+ if (parent.length) {
+ if (parent.scrollTop() >= this._oldHeight) {
+ parent.stop();
+ parent.scrollTop(parent[0].scrollHeight - parent.height());
+ }
+ this._oldHeight = parent[0].scrollHeight - parent.height();
+ }
+ },
+ init: function () {
+ this.updateLogs();
+ },
+ updateLogs: function (name) {
+ if (name && name !== this.getParams().name) {
+ return;
+ }
+ this.setState({
+ logs: ContainerStore.logs(this.getParams().name)
+ });
+ },
+ render: function () {
+ var logs = this.state.logs.map(function (l, i) {
+ return
;
+ });
+ return (
+
+ {logs}
+
+ );
+ }
+});
+
+module.exports = ContainerLogs;
diff --git a/src/ContainerSettings.react.js b/src/ContainerSettings.react.js
new file mode 100644
index 0000000000..3b0ba56e88
--- /dev/null
+++ b/src/ContainerSettings.react.js
@@ -0,0 +1,53 @@
+var _ = require('underscore');
+var React = require('react/addons');
+var Router = require('react-router');
+
+var ContainerSettings = React.createClass({
+ mixins: [Router.State, Router.Navigation],
+ componentWillReceiveProps: function () {
+ this.init();
+ },
+ componentDidMount: function() {
+ this.init();
+ },
+ init: function () {
+ var currentRoute = _.last(this.getRoutes()).name;
+ if (currentRoute === 'containerSettings') {
+ this.transitionTo('containerSettingsGeneral', {name: this.getParams().name});
+ }
+ },
+ render: function () {
+ var container = this.props.container;
+ if (!container) {
+ return (
);
+ }
+ return (
+
+
+
+
+
+ -
+ General
+
+
+
+ -
+ Ports
+
+
+
+ -
+ Volumes
+
+
+
+
+
+
+
+ );
+ }
+});
+
+module.exports = ContainerSettings;
diff --git a/src/ContainerSettingsGeneral.react.js b/src/ContainerSettingsGeneral.react.js
new file mode 100644
index 0000000000..c529b18bea
--- /dev/null
+++ b/src/ContainerSettingsGeneral.react.js
@@ -0,0 +1,234 @@
+var _ = require('underscore');
+var $ = require('jquery');
+var React = require('react/addons');
+var Router = require('react-router');
+var path = require('path');
+var remote = require('remote');
+var rimraf = require('rimraf');
+var fs = require('fs');
+var dialog = remote.require('dialog');
+var ContainerStore = require('./ContainerStore');
+var ContainerUtil = require('./ContainerUtil');
+
+var containerNameSlugify = function (text) {
+ text = text.replace(/^\s+|\s+$/g, ''); // Trim
+ text = text.toLowerCase();
+ // Remove Accents
+ var from = "àáäâèéëêìíïîòóöôùúüûñç·/,:;";
+ var to = "aaaaeeeeiiiioooouuuunc-----";
+ for (var i=0, l=from.length ; i
);
+ }
+ var willBeRenamedAs;
+ var btnSaveName = (
+ Save
+ );
+ if (this.state.slugName) {
+ willBeRenamedAs = (
+ Will be renamed as: {this.state.slugName}
+ );
+ btnSaveName = (
+ Save
+ );
+ }
+ var rename = (
+
+
Container Name
+
+
+ {willBeRenamedAs}
+
+ {btnSaveName}
+
+ );
+ var self = this;
+ var envVars = _.map(this.state.env, function (val, key) {
+ return (
+
+ );
+ });
+ var pendingEnvVars = _.map(this.state.pendingEnv, function (val, key) {
+ return (
+
+ );
+ });
+ return (
+
+ {rename}
+
+
Environment Variables
+
+
+ {envVars}
+ {pendingEnvVars}
+
+
+
Save
+
+
+
+ );
+ }
+});
+
+module.exports = ContainerSettingsGeneral;
diff --git a/src/ContainerSettingsPorts.react.js b/src/ContainerSettingsPorts.react.js
new file mode 100644
index 0000000000..ac0c4cc16b
--- /dev/null
+++ b/src/ContainerSettingsPorts.react.js
@@ -0,0 +1,85 @@
+var _ = require('underscore');
+var React = require('react/addons');
+var Router = require('react-router');
+var exec = require('exec');
+var ContainerStore = require('./ContainerStore');
+var ContainerUtil = require('./ContainerUtil');
+
+var ContainerSettingsPorts = React.createClass({
+ mixins: [Router.State, Router.Navigation],
+ getInitialState: function () {
+ return {
+ ports: {},
+ defaultPort: null
+ };
+ },
+ componentWillReceiveProps: function () {
+ this.init();
+ },
+ componentDidMount: function() {
+ this.init();
+ },
+ init: function () {
+ var container = ContainerStore.container(this.getParams().name);
+ if (!container) {
+ return;
+ }
+ var ports = ContainerUtil.ports(container);
+ var webPorts = ['80', '8000', '8080', '3000', '5000', '2368'];
+ this.setState({
+ ports: ports,
+ defaultPort: _.find(_.keys(ports), function (port) {
+ return webPorts.indexOf(port) !== -1;
+ })
+ });
+ },
+ handleViewLink: function (url) {
+ exec(['open', url], function (err) {
+ if (err) { throw err; }
+ });
+ },
+ handleChangeDefaultPort: function (port, e) {
+ if (e.target.checked) {
+ this.setState({
+ defaultPort: null
+ });
+ } else {
+ this.setState({
+ defaultPort: port
+ });
+ }
+ },
+ render: function () {
+ if (!this.props.container) {
+ return ();
+ }
+ var self = this;
+ var ports = _.map(_.pairs(self.state.ports), function (pair) {
+ var key = pair[0];
+ var val = pair[1];
+ return (
+
+ );
+ });
+ return (
+
+
+
Configure Ports
+
+
+
DOCKER PORT
+
MAC PORT
+
+ {ports}
+
+
+
+ );
+ }
+});
+
+module.exports = ContainerSettingsPorts;
diff --git a/src/ContainerSettingsVolumes.react.js b/src/ContainerSettingsVolumes.react.js
new file mode 100644
index 0000000000..a17d08cc3d
--- /dev/null
+++ b/src/ContainerSettingsVolumes.react.js
@@ -0,0 +1,82 @@
+var _ = require('underscore');
+var React = require('react/addons');
+var Router = require('react-router');
+var remote = require('remote');
+var exec = require('exec');
+var dialog = remote.require('dialog');
+var ContainerStore = require('./ContainerStore');
+
+var ContainerSettingsVolumes = React.createClass({
+ mixins: [Router.State, Router.Navigation],
+ handleChooseVolumeClick: function (dockerVol) {
+ var self = this;
+ dialog.showOpenDialog({properties: ['openDirectory', 'createDirectory']}, function (filenames) {
+ if (!filenames) {
+ return;
+ }
+ var directory = filenames[0];
+ if (directory) {
+ var volumes = _.clone(self.props.container.Volumes);
+ volumes[dockerVol] = directory;
+ var binds = _.pairs(volumes).map(function (pair) {
+ return pair[1] + ':' + pair[0];
+ });
+ ContainerStore.updateContainer(self.props.container.Name, {
+ Binds: binds
+ }, function (err) {
+ if (err) { console.log(err); }
+ });
+ }
+ });
+ },
+ handleOpenVolumeClick: function (path) {
+ exec(['open', path], function (err) {
+ if (err) { throw err; }
+ });
+ },
+ render: function () {
+ if (!this.props.container) {
+ return ();
+ }
+ var self = this;
+ var volumes = _.map(self.props.container.Volumes, function (val, key) {
+ if (!val || val.indexOf(process.env.HOME) === -1) {
+ val = (
+
+ No Folder
+ Change
+
+ );
+ } else {
+ val = (
+
+ {val.replace(process.env.HOME, '~')}
+ Change
+
+ );
+ }
+ return (
+
+ {key}
+ {val}
+
+ );
+ });
+ return (
+
+
+
Configure Volumes
+
+
+
DOCKER FOLDER
+
MAC FOLDER
+
+ {volumes}
+
+
+
+ );
+ }
+});
+
+module.exports = ContainerSettingsVolumes;
diff --git a/src/ContainerUtil.js b/src/ContainerUtil.js
index 8ff36e6620..044ba1652a 100644
--- a/src/ContainerUtil.js
+++ b/src/ContainerUtil.js
@@ -22,7 +22,7 @@ var ContainerUtil = {
if (value && value.length) {
var port = value[0].HostPort;
localUrl = 'http://' + ip + ':' + port;
- localUrlDisplay = ip + ': ' + port;
+ localUrlDisplay = ip + ':' + port;
}
res[dockerPort] = {
url: localUrl,
diff --git a/src/Containers.react.js b/src/Containers.react.js
index 0446677df6..489456641b 100644
--- a/src/Containers.react.js
+++ b/src/Containers.react.js
@@ -20,7 +20,7 @@ var Containers = React.createClass({
ContainerStore.on(ContainerStore.CLIENT_CONTAINER_EVENT, this.updateFromClient);
if (this.state.sorted.length) {
- this.transitionTo('container', {name: this.state.sorted[0].Name});
+ this.transitionTo('containerHome', {name: this.state.sorted[0].Name});
}
},
componentDidUnmount: function () {
@@ -34,7 +34,7 @@ var Containers = React.createClass({
});
if (status === 'destroy') {
if (this.state.sorted.length) {
- this.transitionTo('container', {name: this.state.sorted[0].Name});
+ this.transitionTo('containerHome', {name: this.state.sorted[0].Name});
} else {
this.transitionTo('containers');
}
@@ -46,7 +46,7 @@ var Containers = React.createClass({
sorted: ContainerStore.sorted()
});
if (status === 'create') {
- this.transitionTo('container', {name: name});
+ this.transitionTo('containerHome', {name: name});
}
},
handleScroll: function (e) {
diff --git a/src/NewContainer.react.js b/src/NewContainer.react.js
index fa325242d7..02b3a17a07 100644
--- a/src/NewContainer.react.js
+++ b/src/NewContainer.react.js
@@ -11,7 +11,7 @@ var NewContainer = React.createClass({
getInitialState: function () {
return {
query: '',
- results: ContainerStore.recommended(),
+ results: [],
loading: false,
tags: {},
active: null,
@@ -24,6 +24,7 @@ var NewContainer = React.createClass({
});
this.refs.searchInput.getDOMNode().focus();
ContainerStore.on(ContainerStore.CLIENT_RECOMMENDED_EVENT, this.update);
+ this.update();
},
update: function () {
if (!this.state.query.length) {
@@ -105,8 +106,10 @@ var NewContainer = React.createClass({
render: function () {
var self = this;
var title = this.state.query ? 'Results' : 'Recommended';
- var data = this.state.results.slice(0, 6);
-
+ var data = [];
+ if (this.state.results) {
+ data = this.state.results.slice(0, 6);
+ }
var results;
if (data.length) {
var items = data.map(function (r) {
@@ -173,11 +176,22 @@ var NewContainer = React.createClass({
);
} else {
- results = (
-
-
-
- );
+ if (this.state.results.length === 0 && this.state.query === '') {
+ results = (
+
+ );
+ } else {
+ results = (
+
+
Cannot find a matching image.
+
+ );
+ }
}
var loadingClasses = React.addons.classSet({
hidden: !this.state.loading,
@@ -198,7 +212,7 @@ var NewContainer = React.createClass({
diff --git a/src/Radial.react.js b/src/Radial.react.js
index 011fb1ffd0..7d1ee5bf96 100644
--- a/src/Radial.react.js
+++ b/src/Radial.react.js
@@ -15,7 +15,8 @@ var Radial = React.createClass({
'radial-spinner': this.props.spin,
'radial-negative': this.props.error,
'radial-thick': this.props.thick || false,
- 'radial-gray': this.props.gray || false
+ 'radial-gray': this.props.gray || false,
+ 'radial-transparent': this.props.transparent || false
});
return (
diff --git a/src/Routes.js b/src/Routes.js
index 847fb8d1e6..16d82a5ca2 100644
--- a/src/Routes.js
+++ b/src/Routes.js
@@ -2,6 +2,12 @@ var React = require('react/addons');
var Setup = require('./Setup.react');
var Containers = require('./Containers.react');
var ContainerDetails = require('./ContainerDetails.react');
+var ContainerHome = require('./ContainerHome.react');
+var ContainerLogs = require('./ContainerLogs.react');
+var ContainerSettings = require('./ContainerSettings.react');
+var ContainerSettingsGeneral = require('./ContainerSettingsGeneral.react');
+var ContainerSettingsPorts = require('./ContainerSettingsPorts.react');
+var ContainerSettingsVolumes = require('./ContainerSettingsVolumes.react');
var Preferences = require('./Preferences.react');
var NewContainer = require('./NewContainer.react');
var Router = require('react-router');
@@ -21,7 +27,15 @@ var App = React.createClass({
var routes = (
-
+
+
+
+
+
+
+
+
+
diff --git a/styles/containers.less b/styles/containers.less
index 0c310ce5df..750a29aef8 100644
--- a/styles/containers.less
+++ b/styles/containers.less
@@ -28,7 +28,7 @@
.image-item {
display: flex;
width: 320px;
- height: 170px;
+ height: 166px;
border-radius: 4px;
border: 1px solid @gray-lightest;
background-color: white;
@@ -50,6 +50,10 @@
font-size: 18px;
color: @gray-darkest;
margin-bottom: 5px;
+ width: 190px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
img {
margin-right: 7px;
position: relative;
@@ -59,7 +63,7 @@
.description {
font-size: 12px;
color: @gray-normal;
- height: 70px;
+ height: 65px;
text-overflow: ellipsis;
overflow: hidden;
-webkit-box-orient: vertical;
@@ -136,6 +140,21 @@
flex: 1 auto;
display: flex;
align-items: center;
+ .loader {
+ margin: 0 auto;
+ margin-top: -20%;
+ text-align: center;
+ width: 300px;
+ h2 {
+ margin-bottom: 20px;
+ }
+ }
+ h1 {
+ color: @gray-lightest;
+ font-size: 24px;
+ margin: 0 auto;
+ margin-top: -20%;
+ }
}
}
.new-container-header {
@@ -253,7 +272,7 @@
color: @brand-action;
transition: all 0.25s;
&:hover {
- color: darken(@brand-action, 10%);
+ color: darken(@brand-action, 15%);
}
}
}
@@ -306,6 +325,8 @@
.btn-delete {
font-size: 24px;
color: white;
+ position: relative;
+ z-index: 9999;
}
.state-new {
.at2x('container-white.png', 20px, 20px);
@@ -353,6 +374,7 @@
margin-left: 16px;
.name {
text-overflow: ellipsis;
+ max-width: 140px;
white-space: nowrap;
overflow: hidden;
font-size: 14px;
@@ -364,6 +386,7 @@
font-size: 12px;
font-weight: 400;
text-overflow: ellipsis;
+ max-width: 140px;
white-space: nowrap;
overflow: hidden;
}
@@ -379,6 +402,8 @@
.btn-delete {
font-size: 24px;
color: @gray-lighter;
+ position: relative;
+ z-index: 9999;
}
}
@@ -521,6 +546,9 @@
margin-top: -12px;
.action {
display: inline-block;
+ &.disabled {
+ opacity: 0.3;
+ }
.action-icon {
color: @gray-normal;
font-size: 30px;
@@ -564,6 +592,9 @@
color: white;
background-image: linear-gradient(-180deg, #24B8EB 4%, #24A3EB 100%);
}
+ &.disabled {
+ opacity: 0.5;
+ }
}
}
}
@@ -657,129 +688,155 @@
.left {
width: 60%;
flex-direction: column;
- .web-preview {
- margin-right: 30px;
- .subtext {
- text-align: right;
- color: @gray-lightest;
- margin-top: 2px;
- }
- .widget {
- background-color: white;
- width: 100%;
- height: 100%;
- border-radius: 4px;
- border: 1px solid @gray-lightest;
- position: relative;
- iframe {
- border: 0;
- border-radius: 4px;
- /*width: 100%;
- height: 100%;*/
- position: relative;
- top: -50%;
- left: -50%;
- width: 200%;
- height: 200%;
- transform: scale(0.5);
- }
- .iframe-overlay {
- position: absolute;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- z-index: 100;
- color: transparent;
- transition: all 0.25s;
- .icon {
- margin-top: 40%;
- display: block;
- font-size: 60px;
- text-align: center;
- }
- .text {
- font-size: 20px;
- text-align: center;
- }
- &:hover {
- color: white;
- background-color: @gray-darkest;
- opacity: 0.75;
- }
- }
- }
- }
+ margin-right: 30px;
}
.right {
width: 40%;
flex-direction: column;
- .mini-logs {
- margin-bottom: 50px;
- .widget {
- position: relative;
- border-radius: 4px;
- border: 1px solid @gray-lightest;
- background-color: @gray-darkest;
- color: @gray-lightest;
- height: 100%;
+ }
+ .web-preview {
+ margin-bottom: 50px;
+ .subtext {
+ text-align: right;
+ color: @gray-lightest;
+ margin-top: 2px;
+ transition: all 0.25s;
+ &:hover {
+ color: darken(@gray-lightest, 10%);
+ }
+ }
+ .widget {
+ background-color: white;
+ width: 100%;
+ height: 100%;
+ border-radius: 4px;
+ border: 1px solid @gray-lightest;
+ position: relative;
+ p {
+ font-size: 13px;
+ color: @gray-normal;
padding: 10px;
- overflow: hidden;
+ padding-bottom: 0px;
+ }
+ .ip-port {
+ padding: 20px;
+ padding-top: 5px;
+ color: @gray-darkest;
font-family: Menlo;
- font-size: 8px;
- white-space: pre-wrap;
- p {
- margin-bottom: 0px;
+ -webkit-user-select: text;
+ }
+ iframe {
+ border: 0;
+ border-radius: 4px;
+ position: absolute;
+ top: -50%;
+ left: -50%;
+ width: 200%;
+ height: 200%;
+ transform: scale(0.5);
+ }
+ .iframe-overlay {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ z-index: 100;
+ color: transparent;
+ transition: all 0.25s;
+ .icon {
+ margin-top: 40%;
+ display: block;
+ font-size: 60px;
+ text-align: center;
}
- .mini-logs-overlay {
- position: absolute;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- z-index: 100;
- color: transparent;
- transition: all 0.25s;
- .icon {
- margin-top: 25%;
- display: block;
- font-size: 60px;
- text-align: center;
- }
- .text {
- font-family: "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif;
- font-size: 20px;
- text-align: center;
- }
- &:hover {
- color: white;
- background-color: @gray-darkest;
- opacity: 0.75;
- }
+ .text {
+ font-size: 20px;
+ text-align: center;
+ }
+ &:hover {
+ color: white;
+ background-color: @gray-darkest;
+ opacity: 0.75;
}
}
}
- .folders {
- .subtext {
- text-align: right;
- color: @gray-lightest;
- margin-top: 2px;
+ }
+ .mini-logs {
+ margin-bottom: 50px;
+ .widget {
+ position: relative;
+ border-radius: 4px;
+ border: 1px solid @gray-lightest;
+ background-color: @gray-darkest;
+ color: @gray-lightest;
+ height: 100%;
+ padding: 10px;
+ overflow: hidden;
+ font-family: Menlo;
+ font-size: 7px;
+ white-space: pre;
+ p {
+ margin-bottom: 0px;
}
- .widget {
- padding: 20px 10px;
- background-color: white;
- border-radius: 4px;
- border: 1px solid @gray-lightest;
- display: flex;
- .folder {
- width: 100px;
- img {
- display: block;
- margin: 0 auto;
- }
- .text {
- text-align: center;
- }
+ .mini-logs-overlay {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ z-index: 100;
+ color: transparent;
+ transition: all 0.25s;
+ .icon {
+ margin-top: 25%;
+ display: block;
+ font-size: 60px;
+ text-align: center;
+ }
+ .text {
+ font-family: "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif;
+ font-size: 20px;
+ text-align: center;
+ }
+ &:hover {
+ color: white;
+ background-color: @gray-darkest;
+ opacity: 0.75;
+ }
+ }
+ }
+ }
+ .folders {
+ .subtext {
+ text-align: right;
+ color: @gray-lightest;
+ margin-top: 2px;
+ transition: all 0.25s;
+ &:hover {
+ color: darken(@gray-lightest, 10%);
+ }
+ }
+ .widget {
+ padding: 10px 5px;
+ background-color: white;
+ border-radius: 4px;
+ border: 1px solid @gray-lightest;
+ display: flex;
+ .folder {
+ width: 110px;
+ padding: 5px;
+ &:hover {
+ background-color: #F9F9F9;
+ border-radius: 10px;
+ }
+ img {
+ display: block;
+ margin: 0 auto;
+ }
+ .text {
+ margin-top: 4px;
+ text-align: center;
}
}
}
@@ -799,108 +856,62 @@
}
}
.settings {
- padding: 18px 38px;
- .settings-section {
- margin-bottom: 40px;
- }
- }
- .ports {
- padding: 18px 38px;
- }
- .volumes {
- padding: 18px 38px;
- }
-
- .table {
- margin-bottom: 0;
- .icon-arrow-right {
- color: #aaa;
- margin: 2px 9px 0;
- flex: 0 auto;
- min-width: 13px;
- }
- .btn {
- min-width: 22px;
- margin-left: 10px;
- }
- .table-labels {
- margin-top: 20px;
- flex: 1 auto;
- display: flex;
- font-size: 12px;
- color: @gray-lightest;
- .label-left {
- flex: 0 auto;
- min-width: 80px;
- margin-right: 30px;
- text-align: right;
- }
- .label-right {
- flex: 1 auto;
- display: inline-block;
- width: 40%;
- }
- }
- .table-values {
- flex: 1 auto;
- display: flex;
- flex-direction: row;
- margin: 8px 0;
- .value-left {
- text-align: right;
- min-width: 80px;
- flex: 0 auto;
- }
- .value-right {
- flex: 1 auto;
- -webkit-user-select: text;
- width: 40%;
- }
- }
- .table-new {
- margin-top: 10px;
- flex: 1 auto;
- display: flex;
- input {
+ display: flex;
+ flex: 1 auto;
+ flex-direction: row;
+ .settings-menu {
+ min-width: 160px;
+ ul {
+ position: fixed;
+ margin: 0;
padding: 0;
- font-weight: 400;
- }
- input.new-left {
- flex: 0 auto;
- text-align: right;
- min-width: 80px;
- max-width: 80px;
- }
- .new-right-wrapper {
- position: relative;
+ padding-top: 14px;
+
display: flex;
- flex: 1 auto;
- .new-right-placeholder {
- position: absolute;
- top: 3px;
- left: 0;
- font-weight: 400;
+ flex-direction: column;
+
+ a {
+ min-width: 160px;
+ margin-left: 12px;
+ color: @gray-normal;
+ flex-shrink: 0;
+ cursor: default;
+ outline: none;
+ margin-bottom: 10px;
+ &.active {
+ li {
+ color: white;
+ border-radius: 40px;
+ background-image: linear-gradient(-180deg, #24B8EB 4%, #24A3EB 100%);
+ }
+ }
+ &:hover {
+ text-decoration: none;
+ li {
+ cursor: default;
+ border-radius: 40px;
+ background-color: #F9F9F9;
+ }
+ }
+ &:focus {
+ text-decoration: none;
+ }
}
- input.new-right {
- flex: 1 auto;
- height: 24px;
- position :relative;
- padding-left: 107px;
+ li {
+ vertical-align: middle;
+ padding: 5px 12px;
+ display: flex;
+ flex-direction: row;
}
}
}
-
- &.volumes {
- .label-left {
- min-width: 120px;
- }
- .value-left {
- min-width: 120px;
- }
- .icon {
- color: #aaa;
- margin: 2px 9px 0;
+ .settings-panel {
+ padding-left: 40px;
+ width: 100%;
+ overflow-x: hidden;
+ .settings-section {
+ margin-bottom: 40px;
}
}
}
@@ -909,7 +920,16 @@
.container-name {
margin-bottom: 20px;
input {
- width: 20%;
+ width: 40%;
+ }
+ p {
+ font-weight: 300;
+ margin-top: 5px;
+ color: @gray-lighter;
+ font-size: 12px;
+ strong {
+ font-weight: 500;
+ }
}
}
@@ -923,11 +943,11 @@
.label-key {
display: inline-block;
margin-right: 30px;
- width: 20%;
+ width: 30%;
}
.label-val {
display: inline-block;
- width: 40%;
+ width: 50%;
}
}
.env-vars {
@@ -938,10 +958,109 @@
input {
margin-right: 30px;
&.key {
- width: 20%;
+ width: 30%;
}
&.val {
- width: 40%;
+ width: 50%;
+ }
+ }
+ }
+
+ .table {
+ margin-bottom: 0;
+ .icon-arrow-right {
+ color: #BBB;
+ font-size: 20px;
+ margin: 0px 10px;
+ flex: 0 auto;
+ min-width: 13px;
+ }
+ &.ports {
+ .table-labels {
+ margin-top: 20px;
+ flex: 1 auto;
+ display: flex;
+ font-size: 12px;
+ color: @gray-lightest;
+ .label-left {
+ flex: 0 auto;
+ min-width: 85px;
+ margin-right: 30px;
+ text-align: right;
+ }
+ .label-right {
+ flex: 1 auto;
+ display: inline-block;
+ margin-left: 10px;
+ width: 40%;
+ }
+ }
+ .table-values {
+ flex: 1 auto;
+ display: flex;
+ flex-direction: row;
+ margin: 8px 0;
+ .value-left {
+ text-align: right;
+ min-width: 85px;
+ flex: 0 auto;
+ padding: 0px;
+ }
+ .value-right {
+ flex: 1 auto;
+ -webkit-user-select: text;
+ max-width: 170px;
+ padding: 0px;
+ }
+ label {
+ margin-left: 8px;
+ margin-top: 1px;
+ font-weight: 400;
+ font-size: 13px;
+ }
+ input[type="checked"] {
+
+ }
+ }
+ }
+ &.volumes {
+ .table-labels {
+ margin-top: 20px;
+ flex: 1 auto;
+ display: flex;
+ font-size: 12px;
+ color: @gray-lightest;
+ .label-left {
+ flex: 0 auto;
+ margin-right: 30px;
+ width: 30%;
+ }
+ .label-right {
+ flex: 1 auto;
+ display: inline-block;
+ margin-left: 10px;
+ width: 60%;
+ }
+ }
+ .table-values {
+ flex: 1 auto;
+ display: flex;
+ flex-direction: row;
+ margin: 8px 0;
+ .value-left {
+ width: 30%;
+ flex: 0 auto;
+ padding: 0px;
+ }
+ .value-right {
+ flex: 1 auto;
+ -webkit-user-select: text;
+ width: 60%;
+ padding: 0px;
+ }
+ .btn {
+ margin-left: 10px;
+ }
}
}
}
diff --git a/styles/header.less b/styles/header.less
index 5eaca39d6a..63c7c4a435 100644
--- a/styles/header.less
+++ b/styles/header.less
@@ -4,7 +4,7 @@
position: absolute;
min-width: 100%;
flex: 0;
- min-height: 30px;
+ min-height: 40px;
-webkit-app-region: drag;
-webkit-user-select: none;
// border-bottom: 1px solid #efefef;
diff --git a/styles/radial.less b/styles/radial.less
index 91d2ffb18b..b814eb5b61 100644
--- a/styles/radial.less
+++ b/styles/radial.less
@@ -90,24 +90,11 @@
.inset {
width: @inset-size;
height: @inset-size;
- position: absolute;
margin-left: (@circle-size - @inset-size) / 2.0;
margin-top: (@circle-size - @inset-size) / 2.0;
-
- background-color: @inset-color;
- border-radius: 100%;
.percentage {
- width: @percentage-text-width;
- position: absolute;
top: (@inset-size - @percentage-font-size) / 2.0;
left: (@inset-size - @percentage-text-width) / 2.0;
-
- line-height: 1;
- text-align: center;
-
- color: @brand-primary;
- font-weight: 500;
- font-size: @percentage-font-size;
}
}
}
@@ -116,6 +103,14 @@
background: #EEE;
}
+ &.radial-transparent {
+ @inset-color: #F9F9F9;
+ background: #F9F9F9;
+ .inset {
+ background-color: @inset-color;
+ }
+ }
+
@i: 0;
@increment: 180deg / 100;
.loop (@i) when (@i <= 100) {
diff --git a/styles/theme.less b/styles/theme.less
index 2a404974b3..c31c87d156 100644
--- a/styles/theme.less
+++ b/styles/theme.less
@@ -68,8 +68,8 @@ input[type="text"] {
&:hover,
&:focus {
- border-color: darken(@btn-color, 10%);
- color: darken(@btn-color, 10%);
+ border-color: darken(@btn-color, 15%);
+ color: darken(@btn-color, 15%);
cursor: default;
box-shadow: none;
background: none;
@@ -77,8 +77,8 @@ input[type="text"] {
&:active {
background-color: lighten(@btn-color, 45%);
- border-color: darken(@btn-color, 10%);
- color: darken(@btn-color, 10%);
+ border-color: darken(@btn-color, 15%);
+ color: darken(@btn-color, 15%);
box-shadow: none;
}
@@ -131,11 +131,13 @@ input[type="text"] {
box-shadow: none;
font-weight: 400;
text-shadow: none;
- padding: 4px 14px 4px 14px;
- height: 28px;
+ padding: 5px 14px 5px 14px;
+ height: 30px;
cursor: default;
&.small {
+ font-size: 11px;
+ padding: 3px 8px 3px 8px;
height: 22px;
.icon {
font-size: 10px;
@@ -179,7 +181,7 @@ input[type="text"] {
padding: 6px 7px 6px 7px;
&.small {
width: 22px;
- padding: 2px 5px 3px 5px;
+ padding: 4px 5px 4px 5px;
}
}
}