Merge pull request #17 from vincent99/update-ember

Update ember, cli, handlebars and deprecations
This commit is contained in:
Vincent Fiduccia 2015-01-08 17:36:58 -07:00
commit a6cdf94f93
39 changed files with 364 additions and 225 deletions

View File

@ -29,5 +29,5 @@ indent_size = 2
indent_style = space indent_style = space
indent_size = 2 indent_size = 2
[*.md] [*.{diff,md}]
trim_trailing_whitespace = false trim_trailing_whitespace = false

View File

@ -8,24 +8,27 @@ Perhaps you like managing cattle.
## Usage ## Usage
Prerequisites: Prerequisites:
* [Bower](from http://bower.io/)
* [Git](http://git-scm.com/) * [Git](http://git-scm.com/)
* [Node.js](http://nodejs.org/) (with NPM) * [Node.js](http://nodejs.org/) (with NPM)
* [Bower](http://bower.io/)
If you're on a Mac and use Homebrew, you can follow these steps: If you're on a Mac and use Homebrew, you can follow these steps:
```bash
brew install node watchman
npm install -g bower
```
Setup:
```bash ```bash
git clone 'https://github.com/rancherio/ui' git clone 'https://github.com/rancherio/ui'
cd 'ui' cd 'ui'
git submodule init git submodule init
git submodule update git submodule update
brew install node
npm install npm install
npm install -g bower
bower install bower install
brew install watchman # (or https://facebook.github.io/watchman/docs/install.html)
``` ```
Run development server Run development server:
```bash ```bash
npm start npm start
``` ```
@ -46,6 +49,10 @@ The built-in cattle server expects to be run from `/static/` and hosted on a CDN
### Running Tests ### Running Tests
```bash
npm install -g ember-cli
```
* `ember test` * `ember test`
* `ember test` * `ember test`
* `ember test --server` * `ember test --server`

View File

@ -1,7 +1,7 @@
import Ember from 'ember'; import Ember from 'ember';
import Resolver from 'ember/resolver'; import Resolver from 'ember/resolver';
import loadInitializers from 'ember/load-initializers'; import loadInitializers from 'ember/load-initializers';
import config from 'ui/config/environment'; import config from './config/environment';
Ember.MODEL_FACTORY_INJECTIONS = true; Ember.MODEL_FACTORY_INJECTIONS = true;

View File

@ -26,6 +26,19 @@ var ApikeyController = Cattle.TransitioningResourceController.extend({
this.transitionToRoute('apikey.delete',this.get('model')); this.transitionToRoute('apikey.delete',this.get('model'));
}, },
}, },
availableActions: function() {
var a = this.get('actions');
return [
{ tooltip: 'Edit', icon: 'fa-edit', action: 'edit', enabled: !!a.update },
{ tooltip: 'Activate', icon: 'fa-play', action: 'activate', enabled: !!a.activate },
{ tooltip: 'Deactivate', icon: 'fa-pause', action: 'deactivate', enabled: !!a.deactivate },
{ tooltip: 'Restore', icon: 'fa-ambulance', action: 'restore', enabled: !!a.restore },
{ tooltip: 'Delete', icon: 'fa-trash-o', action: 'promptDelete', enabled: !!a.remove, altAction: 'delete' },
{ tooltip: 'Purge', icon: 'fa-fire', action: 'purge', enabled: !!a.purge },
];
}.property('actions.{update,activate,deactivate,restore,remove,purge}'),
}); });
ApikeyController.reopenClass({ ApikeyController.reopenClass({

View File

@ -4,6 +4,14 @@ export default Cattle.CollectionController.extend({
needs: ['application'], needs: ['application'],
itemController: 'apikey', itemController: 'apikey',
endpoint: function() { endpoint: function() {
return this.get('controllers.application.absoluteEndpoint').replace(/\/+$/,'') + this.get('app.apiEndpoint'); // Strip trailing slash off of the absoluteEndpoint
var url = this.get('controllers.application.absoluteEndpoint').replace(/\/+$/,'');
// Add a single slash
url += '/';
// And strip leading slashes off the API endpoint
url += this.get('app.apiEndpoint').replace(/^\/+/,'');
return url;
}.property('controllers.application.absoluteEndpoint','app.apiEndpoint') }.property('controllers.application.absoluteEndpoint','app.apiEndpoint')
}); });

View File

@ -30,7 +30,7 @@
<th width="140">&nbsp;</th> <th width="140">&nbsp;</th>
</tr> </tr>
{{#each key in dataSource itemController="apikey"}} {{#each key in dataSource itemController="apikey"}}
<tr> <tr class="resource-action-hover">
<td {{bind-attr class="key.stateColor"}}> <td {{bind-attr class="key.stateColor"}}>
<i {{bind-attr class=":fa key.stateIcon"}}></i> {{key.displayState}} <i {{bind-attr class=":fa key.stateIcon"}}></i> {{key.displayState}}
</td> </td>
@ -44,12 +44,7 @@
<div>{{#if key.publicValue}}{{key.publicValue}}{{else}}<span class="text-muted text-italic">No public value</span>{{/if}}</div> <div>{{#if key.publicValue}}{{key.publicValue}}{{else}}<span class="text-muted text-italic">No public value</span>{{/if}}</div>
</td> </td>
<td align="right"> <td align="right">
<button {{bind-attr class=":btn :btn-sm :btn-info key.actions.update::hide"}} {{action "edit"}} tooltip="Edit"><i class="fa fa-edit"></i></button> {{resource-actions model=key}}
<button {{bind-attr class=":btn :btn-sm :btn-info key.actions.activate::hide"}} {{action "activate"}} tooltip="Activate"><i class="fa fa-play"></i></button>
<button {{bind-attr class=":btn :btn-sm :btn-info key.actions.deactivate::hide"}} {{action "deactivate"}} tooltip="Deactivate"><i class="fa fa-pause"></i></button>
<button {{bind-attr class=":btn :btn-sm :btn-info key.actions.remove::hide"}} {{action "delete"}} tooltip="Delete"><i class="fa fa-trash-o"></i></button>
<button {{bind-attr class=":btn :btn-sm :btn-info key.actions.restore::hide"}} {{action "restore"}} tooltip="Restore"><i class="fa fa-ambulance"></i></button>
<button {{bind-attr class=":btn :btn-sm :btn-info key.actions.purge::hide"}} {{action "purge"}} tooltip="Purge"><i class="fa fa-fire"></i></button>
</td> </td>
</tr> </tr>
{{else}} {{else}}

View File

@ -17,6 +17,8 @@ export default Ember.Controller.extend({
absoluteEndpoint: function() { absoluteEndpoint: function() {
var url = this.get('app.endpoint'); var url = this.get('app.endpoint');
// If the URL is relative, add on the current base URL from the browser
if ( url.indexOf('http') !== 0 ) if ( url.indexOf('http') !== 0 )
{ {
url = window.location.origin + '/' + url.replace(/^\/+/,''); url = window.location.origin + '/' + url.replace(/^\/+/,'');
@ -25,6 +27,9 @@ export default Ember.Controller.extend({
url = url + "/"; url = url + "/";
} }
// Url must end in a single slash
url = url.replace(/\/+$/,'') + '/';
return url; return url;
}.property('app.endpoint'), }.property('app.endpoint'),
}); });

View File

@ -4,6 +4,8 @@ export default Ember.Component.extend({
icon: 'fa-square', icon: 'fa-square',
tooltip: '', tooltip: '',
enabled: true, enabled: true,
actionArg: null,
altActionArg: null,
tagName: 'button', tagName: 'button',
type: 'button', type: 'button',
@ -12,13 +14,13 @@ export default Ember.Component.extend({
attributeBindings: ['tooltip'], attributeBindings: ['tooltip'],
click : function(event) { click : function(event) {
if ( event.altKey ) if ( event.altKey && this.get('altActionArg'))
{ {
this.sendAction('altAction'); this.sendAction('action', this.get('altActionArg'));
} }
else else
{ {
this.sendAction(); this.sendAction('action', this.get('actionArg'));
} }
}, },

View File

@ -0,0 +1,6 @@
import Ember from 'ember';
export default Ember.Component.extend({
model: null,
classNames: ['instance','resource-action-hover'],
});

View File

@ -0,0 +1,17 @@
<div {{bind-attr class=":instance-name model.stateColor"}}>
<i {{bind-attr class=":fa model.stateIcon" tooltip=model.displayState}}></i>
{{#link-to "container" model.id}}{{model.displayName}}{{/link-to}}
</div>
{{resource-actions model=model}}
<div {{bind-attr class="isOn::text-muted"}}>{{model.displayIp}}</div>
<div {{bind-attr class=":force-wrap model.isError:text-danger:text-muted model.showTransitioningMessage::hide"}}>
{{model.transitioningMessage}}
</div>
<div {{bind-attr class="model.isTransitioning::hide :progress :progress-striped :active"}}>
<div class="progress-bar" role="progressbar" aria-valuemin="0" aria-valuemax="100" {{bind-attr aria-valuenow=model.displayProgress style=model.progressStyle}}>
<span class="sr-only">{{model.displayProgress}}% Complete</span>
</div>
</div>

View File

@ -0,0 +1,6 @@
import Ember from 'ember';
export default Ember.Component.extend({
model: null,
classNames: ['host','resource-action-hover'],
});

View File

@ -0,0 +1,25 @@
<div class="host-header resource-action-hover">
<div {{bind-attr class=":host-name model.stateColor"}}>
<i {{bind-attr class=":fa model.stateIcon" tooltip=model.displayState}}></i>
{{#link-to "host.index" model.id}}
{{model.displayName}}
{{/link-to}}
<div class="host-ip">{{model.displayIp}}</div>
</div>
{{resource-actions model=model}}
</div>
<div class="no-resource-action-hover">
{{#each item in model.instances itemController="container"}}
{{container-widget model=item}}
{{else}}
<div class="text-center text-muted">No containers yet.</div>
{{/each}}
</div>
{{#link-to "hosts.containerNew" (query-params hostId=model.id tab="basic") tagName="div" classNames="add-to-host" tooltip="Create Container"}}
<span class="fa-stack fa-lg">
<i class="fa fa-circle-thin fa-stack-2x"></i>
<i class="fa fa-plus fa-stack-1x"></i>
</span>
{{/link-to}}

View File

@ -0,0 +1,21 @@
import Ember from 'ember';
export default Ember.Component.extend({
model: null,
isDetail: false,
classNames: ['resource-actions'],
activeActions: function() {
var detailed = this.get('isDetail');
return (this.get('model.availableActions')||[]).filter(function(act) {
return Ember.get(act,'enabled') && (detailed || !Ember.get(act,'detail'));
});
}.property('model.availableActions.[]','model.availableActions.@each.enabled','isDetail'),
actions: {
clicked: function(actionName) {
this.get('model').send(actionName);
}
}
});

View File

@ -0,0 +1,9 @@
{{#each item in activeActions}}
{{action-button
tooltip=item.tooltip
icon=item.icon
action="clicked"
actionArg=item.action
altActionArg=item.altAction
}}
{{/each}}

View File

@ -1,3 +1,4 @@
import Ember from 'ember';
import Cattle from 'ui/utils/cattle'; import Cattle from 'ui/utils/cattle';
var ContainerController = Cattle.TransitioningResourceController.extend({ var ContainerController = Cattle.TransitioningResourceController.extend({
@ -29,19 +30,46 @@ var ContainerController = Cattle.TransitioningResourceController.extend({
return this.doAction('purge'); return this.doAction('purge');
}, },
redirectTo: function(name) {
// @TODO Fix this hackery for nested components...
// http://emberjs.jsbin.com/mecesakase
if ( Ember.Component.detectInstance(this.get('target')) )
{
this.set('target', window.l('router:main'));
}
this.transitionToRoute(name, this.get('id'));
},
shell: function() { shell: function() {
this.transitionToRoute('container.shell', this.get('model')); this.send('redirectTo','container.shell');
}, },
edit: function() { edit: function() {
this.transitionToRoute('container.edit', this.get('model')); this.send('redirectTo','container.edit');
}, },
promptDelete: function() { promptDelete: function() {
this.transitionToRoute('container.delete', this.get('model')); this.send('redirectTo','container.delete');
}, },
}, },
availableActions: function() {
var a = this.get('actions');
return [
{ tooltip: 'Edit', icon: 'fa-edit', action: 'edit', enabled: !!a.update },
{ tooltip: 'View in API', icon: 'fa-external-link', action: 'goToApi', enabled: true, detail: true },
{ tooltip: 'Execute Shell', icon: 'fa-terminal', action: 'shell', enabled: !!a.execute },
{ tooltip: 'Restart', icon: 'fa-refresh', action: 'restart', enabled: !!a.restart },
{ tooltip: 'Start', icon: 'fa-arrow-up', action: 'start', enabled: !!a.start },
{ tooltip: 'Stop', icon: 'fa-arrow-down', action: 'stop', enabled: !!a.stop },
{ tooltip: 'Restore', icon: 'fa-ambulance', action: 'restore', enabled: !!a.restore },
{ tooltip: 'Delete', icon: 'fa-trash-o', action: 'promptDelete', enabled: this.get('canDelete'), altAction: 'delete' },
{ tooltip: 'Purge', icon: 'fa-fire', action: 'purge', enabled: !!a.purge },
];
}.property('actions.{update,execute,restart,start,stop,restore,purge}','canDelete'),
isOn: function() { isOn: function() {
return ['running','updating-running','migrating','restarting'].indexOf(this.get('state')) >= 0; return ['running','updating-running','migrating','restarting'].indexOf(this.get('state')) >= 0;
}.property('state'), }.property('state'),

View File

@ -16,7 +16,7 @@
{{transitioningMessage}} {{transitioningMessage}}
</div> </div>
<div class="instance-actions"> <div class="instance-actions">
{{partial "container/actions"}} {{resource-actions model=this isDetail=true}}
</div> </div>
</div> </div>
</div> </div>

View File

@ -2,16 +2,39 @@ import Cattle from 'ui/utils/cattle';
var HostController = Cattle.TransitioningResourceController.extend({ var HostController = Cattle.TransitioningResourceController.extend({
actions: { actions: {
activate: function() { return this.doAction('activate'); }, activate: function() {
deactivate: function() { return this.doAction('deactivate'); }, return this.doAction('activate');
delete: function() { return this.delete(); }, },
purge: function() { return this.doAction('purge'); },
deactivate: function() {
return this.doAction('deactivate');
},
delete: function() {
return this.delete();
},
purge: function() {
return this.doAction('purge');
},
promptDelete: function() { promptDelete: function() {
this.transitionToRoute('host.delete', this.get('model')); this.transitionToRoute('host.delete', this.get('model'));
}, },
}, },
availableActions: function() {
var a = this.get('actions');
return [
{ tooltip: 'View in API', icon: 'fa-external-link', action: 'goToApi', enabled: true, detail: true },
{ tooltip: 'Activate', icon: 'fa-arrow-up', action: 'activate', enabled: !!a.activate },
{ tooltip: 'Deactivate', icon: 'fa-arrow-down', action: 'deactivate', enabled: !!a.deactivate },
{ tooltip: 'Delete', icon: 'fa-trash-o', action: 'promptDelete', enabled: !!a.remove, altAction: 'delete' },
{ tooltip: 'Purge', icon: 'fa-fire', action: 'purge', enabled: !!a.purge },
];
}.property('actions.{activate,deactivate,remove,purge}'),
displayIp: function() { displayIp: function() {
var obj = (this.get('ipAddresses')||[]).get('firstObject'); var obj = (this.get('ipAddresses')||[]).get('firstObject');
if ( obj ) if ( obj )

View File

@ -1,19 +0,0 @@
<div {{bind-attr class=":instance-name stateColor"}}>
<i {{bind-attr class=":fa stateIcon" tooltip=displayState}}></i>
{{#link-to "container" this.id}}{{displayName}}{{/link-to}}
</div>
<div class="instance-actions">
{{partial "container/actions"}}
</div>
<div {{bind-attr class="isOn::text-muted"}}>{{displayIp}}</div>
<div {{bind-attr class=":force-wrap isError:text-danger:text-muted showTransitioningMessage::hide"}}>
{{transitioningMessage}}
</div>
<div {{bind-attr class="isTransitioning::hide :progress :progress-striped :active"}}>
<div class="progress-bar" role="progressbar" aria-valuemin="0" aria-valuemax="100" {{bind-attr aria-valuenow=displayProgress style=progressStyle}}>
<span class="sr-only">{{displayProgress}}% Complete</span>
</div>
</div>

View File

@ -1,7 +0,0 @@
import Ember from 'ember';
export default Ember.View.extend({
classNames: ['instance'],
tagName: 'DIV',
templateName: 'host/instance',
});

View File

@ -24,7 +24,7 @@
{{transitioningMessage}} {{transitioningMessage}}
</div> </div>
<div class="host-actions"> <div class="host-actions">
{{partial "host/actions"}} {{resource-actions model=this}}
</div> </div>
</div> </div>
</div> </div>
@ -44,7 +44,9 @@
<label>Containers</label> <label>Containers</label>
{{#each col in view.columns}} {{#each col in view.columns}}
<div class="instance-column"> <div class="instance-column">
{{each col itemViewClass="host/instance" itemController="container"}} {{#each item in col itemController="container"}}
{{container-widget model=item}}
{{/each}}
</div> </div>
{{else}} {{else}}
<div style="padding: 20px; text-align: center;" class="text-muted"> <div style="padding: 20px; text-align: center;" class="text-muted">

View File

@ -10,7 +10,9 @@
{{each host.instances itemViewClass="instanceDot" itemController="container"}} {{each host.instances itemViewClass="instanceDot" itemController="container"}}
{{/each}} {{/each}}
{{else}} {{else}}
{{each col itemViewClass="hosts/item" itemController="host"}} {{#each host in col itemController="host"}}
{{host-widget model=host}}
{{/each}}
{{/if}} {{/if}}
</div> </div>
{{/each}} {{/each}}

View File

@ -1,26 +0,0 @@
<div class="host-header">
<div {{bind-attr class=":host-name stateColor"}}>
<i {{bind-attr class=":fa stateIcon" tooltip="displayState"}}></i>
{{#link-to "host.index" this.id}}
{{displayName}}
{{/link-to}}
<div class="host-ip">{{displayIp}}</div>
</div>
<div class="host-actions">
{{partial "host/actions"}}
</div>
</div>
{{#each instances itemController="container"}}
{{view "host/instance"}}
{{else}}
<div class="text-center text-muted">No containers yet.</div>
{{/each}}
{{#link-to "hosts.containerNew" (query-params hostId=this.id tab="basic") tagName="div" classNames="add-to-host" tooltip="Create Container"}}
<span class="fa-stack fa-lg">
<i class="fa fa-circle-thin fa-stack-2x"></i>
<i class="fa fa-plus fa-stack-1x"></i>
</span>
{{/link-to}}

View File

@ -1,6 +0,0 @@
import Ember from 'ember';
export default Ember.View.extend({
classNames: ['host'],
templateName: 'hosts/item',
});

View File

@ -22,6 +22,16 @@ var VolumeController = Cattle.TransitioningResourceController.extend({
}, },
}, },
availableActions: function() {
var a = this.get('actions');
return [
{ tooltip: 'Restore', icon: 'fa-ambulance', action: 'restore', enabled: !!a.restore },
{ tooltip: 'Delete', icon: 'fa-trash-o', action: 'promptDelete', enabled: this.get('canDelete'), altAction: 'delete' },
{ tooltip: 'Purge', icon: 'fa-fire', action: 'purge', enabled: !!a.purge },
];
}.property('actions.{restore,purge}','canDelete'),
displayUri: function() { displayUri: function() {
return (this.get('uri')||'').replace(/^file:\/\//,''); return (this.get('uri')||'').replace(/^file:\/\//,'');
}.property('uri'), }.property('uri'),

View File

@ -1,5 +1,6 @@
<section> <section>
<table class="table fixed" style="margin-bottom: 0"> <table class="table fixed" style="margin-bottom: 0">
<tbody>
<tr> <tr>
<th width="120">State</th> <th width="120">State</th>
<th>Name</th> <th>Name</th>
@ -10,7 +11,9 @@
<tr class="no-lines"> <tr class="no-lines">
<th colspan="5" class="text-muted">Host Path <th colspan="5" class="text-muted">Host Path
</tr> </tr>
</tbody>
{{#each volume in nonRootVolumes itemController="volume"}} {{#each volume in nonRootVolumes itemController="volume"}}
<tbody class="resource-action-hover">
<tr> <tr>
<td {{bind-attr class="volume.stateColor"}}> <td {{bind-attr class="volume.stateColor"}}>
<i {{bind-attr class=":fa volume.stateIcon"}}></i> {{volume.displayState}} <i {{bind-attr class=":fa volume.stateIcon"}}></i> {{volume.displayState}}
@ -32,17 +35,16 @@
<div class="text-muted">{{date-calendar volume.created}}</div> <div class="text-muted">{{date-calendar volume.created}}</div>
</td> </td>
<td align="right"> <td align="right" rowspan="2">
<button type="button" {{bind-attr class=":btn :btn-sm :btn-info volume.canDelete::hide"}} {{action "promptDelete"}} tooltip="Delete"><i class="fa fa-trash-o"></i></button> {{resource-actions model=volume}}
<button type="button" {{bind-attr class=":btn :btn-sm :btn-info volume.actions.restore::hide"}} {{action "restore"}} tooltip="Restore"><i class="fa fa-ambulance"></i></button>
<button type="button" {{bind-attr class=":btn :btn-sm :btn-info volume.actions.purge::hide"}} {{action "purge"}} tooltip="Purge"><i class="fa fa-fire"></i></button>
</td> </td>
</tr> </tr>
<tr class="no-lines"> <tr class="no-lines">
<td colspan="5" class="text-muted clip"> <td colspan="4" class="text-muted clip">
{{volume.displayUri}} {{volume.displayUri}}
</td> </td>
</tr> </tr>
</tbody>
{{/each}} {{/each}}
</table> </table>
</section> </section>

View File

@ -5,6 +5,7 @@
@import "app/styles/growl"; @import "app/styles/growl";
@import "app/styles/resource-actions";
@import "app/styles/layout"; @import "app/styles/layout";
@import "app/styles/overlay"; @import "app/styles/overlay";
@import "app/styles/progress-bar"; @import "app/styles/progress-bar";

View File

@ -72,7 +72,8 @@ H1, H2, H3, H4, H5, H6 {
.table.no-lines > TBODY > TR > TH, .table.no-lines > TBODY > TR > TH,
.table.no-lines > TBODY > TR > TD, .table.no-lines > TBODY > TR > TD,
.table.no-lines > TFOOT > TR > TH, .table.no-lines > TFOOT > TR > TH,
.table.no-lines > TFOOT > TR > TD { .table.no-lines > TFOOT > TR > TD,
.table > TBODY + TBODY {
border-top: none; border-top: none;
} }

View File

@ -99,33 +99,6 @@ $instance-column-width: $host-column-width - 20px;
color: black; color: black;
} }
.host .host-actions,
.instance .instance-actions {
position: absolute;
top: 0;
right: 0;
}
.host-actions,
.instance-actions {
BUTTON {
color: #444;
padding: 0;
}
BUTTON:hover {
color: #1d6fa5;
}
}
.instance-actions {
padding: 5px 10px 5px 0;
}
.instance-detail .instance-actions {
padding: 0;
}
.instance { .instance {
position: relative; position: relative;
border: 1px solid #e7eaee; border: 1px solid #e7eaee;
@ -179,22 +152,6 @@ $instance-column-width: $host-column-width - 20px;
} }
} }
/* Hover actions on devices with mice, always shown on devices with touch */
.no-touch {
.host-actions,
.instance-actions {
display: none;
}
}
.host:hover .host-actions,
.instance:hover .instance-actions,
.host-detail .host-actions,
.instance-detail .instance-actions {
display: block;
}
.dot { .dot {
float: left; float: left;
margin: 2px; margin: 2px;
@ -218,3 +175,14 @@ $instance-column-width: $host-column-width - 20px;
position: absolute; position: absolute;
margin: 10px 0; margin: 10px 0;
} }
.host .resource-actions,
.instance .resource-actions {
position: absolute;
top: 0;
right: 0;
}
.instance .resource-actions {
padding: 5px 10px 5px 0;
}

View File

@ -0,0 +1,46 @@
.resource-actions {
BUTTON {
color: #444;
padding: 0;
}
BUTTON:hover {
color: #1d6fa5;
}
}
/*
Hover actions on devices with mice, always shown on devices with touch.
Put .resource-action-hover on a parent node that you want to trigger display of the actions on.
If there are nested nodes with actions, put .no-resource-action-hover on a node that wraps the inner nodes
to prevent hover on the parent from triggering hover on the children.
*/
.no-touch {
.resource-action-hover {
.resource-actions {
display: none;
}
&:hover {
.resource-actions {
display: block;
}
.no-resource-action-hover {
.resource-actions {
display: none;
}
}
}
.no-resource-action-hover {
.resource-action-hover:hover {
.resource-actions {
display: block;
}
}
}
}
}

View File

@ -1,9 +0,0 @@
{{action-button enabled=actions.update tooltip="Edit" icon="fa-edit" action="edit"}}
{{action-button enabled=view.isDetail tooltip="View in API" icon="fa-external-link" action="goToApi"}}
{{action-button enabled=actions.execute tooltip="Execute Shell" icon="fa-terminal" action="shell"}}
{{action-button enabled=actions.restart tooltip="Restart" icon="fa-refresh" action="restart"}}
{{action-button enabled=actions.start tooltip="Start" icon="fa-arrow-up" action="start"}}
{{action-button enabled=actions.stop tooltip="Stop" icon="fa-arrow-down" action="stop"}}
{{action-button enabled=actions.restore tooltip="Restore" icon="fa-ambulance" action="restore"}}
{{action-button enabled=canDelete tooltip="Delete" icon="fa-trash-o" action="promptDelete" altAction="delete"}}
{{action-button enabled=actions.purge tooltip="Purge" icon="fa-fire" action="purge"}}

View File

@ -1,5 +0,0 @@
{{action-button enabled=view.isDetail tooltip="View in API" icon="fa-external-link" action="goToApi"}}
{{action-button enabled=actions.activate tooltip="Activate" icon="fa-arrow-up" action="activate"}}
{{action-button enabled=actions.deactivate tooltip="Deactivate" icon="fa-arrow-down" action="deactivate"}}
{{action-button enabled=actions.remove tooltip="Delete" icon="fa-trash-o" action="promptDelete" altAction="delete"}}
{{action-button enabled=actions.purge tooltip="Purge" icon="fa-fire" action="purge"}}

View File

@ -9,12 +9,12 @@
<th width="150">Size</th> <th width="150">Size</th>
<th>Usage</th> <th>Usage</th>
</tr> </tr>
{{#each view.stats.filesystem}} {{#each fs in view.stats.filesystem}}
<tr> <tr>
<td>{{device}}</td> <td>{{fs.device}}</td>
<td>{{size_gb}} GB</td> <td>{{fs.size_gb}} GB</td>
<td> <td>
{{progress-bar value=used_percent textSuffix="% Used"}} {{progress-bar value=fs.used_percent textSuffix="% Used"}}
</td> </td>
</tr> </tr>
{{/each}} {{/each}}

View File

@ -200,6 +200,24 @@ var TransitioningResourceController = ResourceController.extend({
return model.doAction.apply(model,arguments); return model.doAction.apply(model,arguments);
}, },
availableActions: function() {
/*
Override me and return [
{
enabled: true/false, // Whether it's enabled or greyed out
detail: true/false, // If true, this action will only be shown on detailed screens
tooltip: 'Delete', // Tooltip shown on hover
icon: 'fa-trash-o', // Icon shown on screen
action: 'promptDelete', // Action to call on the controller when clicked
altAction: 'delete' // Action to call on the controller when alt+clicked
},
...
]
*/
return [];
},
displayProgress: function() { displayProgress: function() {
var progress = this.get('transitioningProgress'); var progress = this.get('transitioningProgress');
if ( isNaN(progress) || !progress ) if ( isNaN(progress) || !progress )

View File

@ -1,9 +1,9 @@
{ {
"name": "ui", "name": "ui",
"dependencies": { "dependencies": {
"handlebars": "~1.3.0", "ember": "1.9.1",
"handlebars": "2.0.0",
"jquery": "^1.11.1", "jquery": "^1.11.1",
"ember": "1.8.1",
"ember-data": "1.0.0-beta.12", "ember-data": "1.0.0-beta.12",
"ember-resolver": "~0.1.10", "ember-resolver": "~0.1.10",
"loader.js": "stefanpenner/loader.js#1.0.1", "loader.js": "stefanpenner/loader.js#1.0.1",

View File

@ -18,18 +18,17 @@
"author": "Rancher Labs", "author": "Rancher Labs",
"license": "Apache 2", "license": "Apache 2",
"devDependencies": { "devDependencies": {
"ember-api-store": "^1.0.6",
"body-parser": "^1.2.0", "body-parser": "^1.2.0",
"broccoli-asset-rev": "^1.0.0", "broccoli-asset-rev": "^1.0.0",
"broccoli-ember-hbs-template-compiler": "1.6.2",
"broccoli-sass": "^0.3.3", "broccoli-sass": "^0.3.3",
"connect-restreamer": "^1.0.1", "connect-restreamer": "^1.0.1",
"ember-cli": "^0.1.4", "ember-api-store": "^1.0.6",
"ember-cli": "^0.1.5",
"ember-cli-content-security-policy": "0.3.0", "ember-cli-content-security-policy": "0.3.0",
"ember-cli-dependency-checker": "0.0.6", "ember-cli-dependency-checker": "0.0.7",
"ember-cli-esnext": "0.1.1", "ember-cli-esnext": "0.1.1",
"ember-cli-font-awesome": "0.0.4", "ember-cli-font-awesome": "0.0.4",
"ember-cli-htmlbars": "^0.6.0",
"ember-cli-inject-live-reload": "^1.3.0", "ember-cli-inject-live-reload": "^1.3.0",
"ember-cli-inline-content": "^0.3.1", "ember-cli-inline-content": "^0.3.1",
"ember-cli-moment": "0.0.1", "ember-cli-moment": "0.0.1",

View File

@ -4,16 +4,16 @@ import Router from '../../router';
import config from '../../config/environment'; import config from '../../config/environment';
export default function startApp(attrs) { export default function startApp(attrs) {
var App; var application;
var attributes = Ember.merge({}, config.APP); var attributes = Ember.merge({}, config.APP);
attributes = Ember.merge(attributes, attrs); // use defaults, but you can override; attributes = Ember.merge(attributes, attrs); // use defaults, but you can override;
Ember.run(function() { Ember.run(function() {
App = Application.create(attributes); application = Application.create(attributes);
App.setupForTesting(); application.setupForTesting();
App.injectTestHelpers(); application.injectTestHelpers();
}); });
return App; return application;
} }

View File

@ -0,0 +1,21 @@
import {
moduleForComponent,
test
} from 'ember-qunit';
moduleForComponent('resource-actions', 'ResourceActionsComponent', {
// specify the other units that are required for this test
// needs: ['component:foo', 'helper:bar']
});
test('it renders', function() {
expect(2);
// creates the component instance
var component = this.subject();
equal(component._state, 'preRender');
// appends the component to the page
this.append();
equal(component._state, 'inDOM');
});

View File

@ -1,12 +0,0 @@
import {
moduleFor,
test
} from 'ember-qunit';
moduleFor('view:host/instance', 'HostInstanceView');
// Replace this with your real tests.
test('it exists', function() {
var view = this.subject();
ok(view);
});

View File

@ -1,12 +0,0 @@
import {
moduleFor,
test
} from 'ember-qunit';
moduleFor('view:hosts/item', 'HostsItemView');
// Replace this with your real tests.
test('it exists', function() {
var view = this.subject();
ok(view);
});