UI: Add quick view to table list

For any list view that has a corresponding detail view, adds a tooltip
to display the compact overview of the detail view contents, including
all available actions. This avoids a user from having to click into a
new panel to see more details and actions for an item.

This will happen automatically for any list view with a 'detailView'
sub-option, and will append a new column to the end of each item
row. If 'noCompact: true' is specified in the detailView's options,
then the quick view will not be rendered.

** Note: This also removes the existing list actions for the instances
         and zones tables.

Example, to show a quick view:

listView: {
  detailView: {
    // Specify noCompact: true to not render a quick view
    //
    // noCompact: true
    ...
  },
  ...
}

Conflicts:
	ui/scripts/ui/widgets/detailView.js
This commit is contained in:
Brian Federle 2012-10-09 11:19:12 -07:00
parent a5edef06c9
commit 991557bfb6
10 changed files with 655 additions and 348 deletions

View File

@ -17,6 +17,12 @@
#new labels (begin) **********************************************************************************************
label.migrate.to.host=Migrate to host
label.migrate.to.storage=Migrate to storage
label.stop=Stop
label.reboot=Reboot
label.destroy=Destroy
label.restore=Restore
label.isolation.uri=Isolation URI
label.broadcast.uri=Broadcast URI
#new labels (end) ************************************************************************************************

View File

@ -74,6 +74,7 @@ body.install-wizard {
display: none;
}
/*Table*/
table {
width: 740px;
max-width: 777px;
@ -140,10 +141,11 @@ table tbody td.loading {
border-top: 1px solid #FBFBFB;
}
/** Actions table cell*/
table tbody td.actions {
width: 155px;
max-width: 155px !important;
min-width: 155px !important;
width: 130px;
max-width: 130px !important;
min-width: 130px !important;
vertical-align: middle;
}
@ -155,6 +157,44 @@ table tbody td.actions input {
margin: 11px 0 0px;
}
/** Quick view table cell*/
table tbody td.quick-view,
table thead th.quick-view {
min-width: 58px;
max-width: 58px !important;
width: 58px !important;
height: 14px !important;
text-indent: 7px;
}
table tbody td.quick-view {
cursor: pointer;
}
table tbody td.quick-view .icon {
margin-left: 22px;
margin-top: 3px;
padding: 0px 0px 6px 12px;
background: url(../images/sprites.png) no-repeat -44px -62px;
}
table tbody td.quick-view:hover .icon {
background-position: -44px -644px;
}
table tbody tr.loading td.quick-view .icon {
display: none;
}
table tbody tr.loading td.quick-view {
cursor: default;
}
table tbody tr.loading td.quick-view .loading {
background-position: center center;
}
/** Row styling*/
table tbody tr {
border-left: 1px solid #C4C5C5;
border-right: 1px solid #C4C5C5;
@ -220,6 +260,7 @@ table th div.ui-resizable-handle {
float: right;
}
/** Header, misc*/
#header,
#navigation {
/*+text-shadow:0px -1px 1px #000000;*/
@ -1229,7 +1270,9 @@ div.panel div.list-view div.fixed-header table {
}
div.list-view td.state {
width: 129px;
width: 120px;
min-width: 120px;
max-width: 120px;
}
div.list-view td.first {
@ -1241,7 +1284,7 @@ div.list-view td.first:hover {
}
div.list-view td.state span {
padding: 0 0 0 26px;
padding: 1px 0 0 18px;
text-align: center;
width: 80px;
/*+text-shadow:0px 1px 1px #FFFFFF;*/
@ -1266,6 +1309,241 @@ div.list-view td.state.off span {
background-position: 1px -496px;
}
/** Quick view tooltip*/
.quick-view-tooltip {
width: 400px;
display: inline-block;
margin-left: -359px;
padding-top: 50px;
}
.quick-view-tooltip > div.title {
width: 376px;
position: absolute;
top: 71px;
left: 10px;
color: #5A6977;
/*+text-shadow:0px 1px #EAEAEA;*/
-moz-text-shadow: 0px 1px #EAEAEA;
-webkit-text-shadow: 0px 1px #EAEAEA;
-o-text-shadow: 0px 1px #EAEAEA;
text-shadow: 0px 1px #EAEAEA;
}
.quick-view-tooltip > div.title .icon {
position: relative;
top: -3px;
background: url(../images/sprites.png) no-repeat -42px -67px;
float: right;
padding: 5px 21px 0 1px;
}
.quick-view-tooltip .loading-overlay {
top: 94px;
height: 57px;
left: 1px;
/*+opacity:35%;*/
filter: alpha(opacity=35);
-ms-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=35);
-moz-opacity: 0.35;
opacity: 0.35;
}
.quick-view-tooltip > div.title span.title {
}
.quick-view-tooltip .container {
border: 1px solid #9EA2A5;
background: #CCCFD6;
width: 400px;
min-height: 100px;
height: auto;
overflow: auto;
display: inline-block;
/*+box-shadow:0px 7px 9px #676F76;*/
-moz-box-shadow: 0px 7px 9px #676F76;
-webkit-box-shadow: 0px 7px 9px #676F76;
-o-box-shadow: 0px 7px 9px #676F76;
box-shadow: 0px 7px 9px #676F76;
}
/*** Quick view detail view*/
.quick-view-tooltip .detail-view {
}
.quick-view-tooltip .detail-view .main-groups {
width: 394px;
height: 132px;
position: absolute;
top: 55px;
padding-top: 7px;
border: 1px solid #808080;
border-left: none;
border-right: none;
overflow: hidden;
/*+box-shadow:0px 1px #E6E6E6;*/
-moz-box-shadow: 0px 1px #E6E6E6;
-webkit-box-shadow: 0px 1px #E6E6E6;
-o-box-shadow: 0px 1px #E6E6E6;
box-shadow: 0px 1px #E6E6E6;
}
.quick-view-tooltip .detail-view .actions {
}
.quick-view-tooltip .detail-view .tagger {
display: none;
}
.quick-view-tooltip .detail-view ul {
display: none !important;
}
.quick-view-tooltip .detail-view .ui-tabs-panel {
display: inline-block;
width: 100% !important;
float: left;
height: auto;
overflow: hidden;
}
.quick-view-tooltip .detail-view .details {
display: inline-block;
height: auto;
padding-bottom: 209px;
}
.quick-view-tooltip .detail-view .detail-group {
width: 365px;
margin: 0;
padding: 0;
left: -9px;
background: none;
border: none;
}
.quick-view-tooltip .detail-view .detail-group table {
margin: 0;
border: none;
background: none;
}
.quick-view-tooltip .detail-view .detail-group table tr {
background: none;
}
.quick-view-tooltip .detail-view .detail-group table td.name {
color: #3E5F7F !important;
padding: 0px 29px 0px 5px !important;
font-size: 13px;
/*+text-shadow:0px 1px #DBDBDB;*/
-moz-text-shadow: 0px 1px #DBDBDB;
-webkit-text-shadow: 0px 1px #DBDBDB;
-o-text-shadow: 0px 1px #DBDBDB;
text-shadow: 0px 1px #DBDBDB;
}
.quick-view-tooltip .detail-view .detail-group table td.value {
font-size: 12px;
/*+text-shadow:0px 1px #EAEAEA;*/
-moz-text-shadow: 0px 1px #EAEAEA;
-webkit-text-shadow: 0px 1px #EAEAEA;
-o-text-shadow: 0px 1px #EAEAEA;
text-shadow: 0px 1px #EAEAEA;
}
.quick-view-tooltip .detail-view .detail-group table td.value input[type=text] {
width: 258px;
height: 10px;
margin-left: 0px;
}
.quick-view-tooltip .detail-view .detail-group .main-groups table td.value span {
height: 15px;
top: 0px;
}
.quick-view-tooltip .detail-view .detail-group.actions {
position: relative;
top: 202px;
float: left;
width: 100%;
height: auto;
}
.quick-view-tooltip .detail-view .detail-group.actions .button {
top: 160px;
}
.quick-view-tooltip .detail-view .detail-group.actions .action.text {
width: 112px;
height: 41px;
background: none;
border: none;
float: left;
margin-left: 5px;
display: inline-block;
}
.quick-view-tooltip .detail-view .detail-group.actions .action.text:hover {
/*+box-shadow:none;*/
-moz-box-shadow: none;
-webkit-box-shadow: none;
-o-box-shadow: none;
box-shadow: none;
}
.quick-view-tooltip .detail-view .detail-group.actions .action.text .icon {
display: block;
float: left;
width: 4px;
}
.quick-view-tooltip .detail-view .detail-group.actions .action.text .label {
width: 81px;
display: block;
float: right;
font-size: 11px;
color: #454C53;
/*+text-shadow:0px 1px #FFFFFF;*/
-moz-text-shadow: 0px 1px #FFFFFF;
-webkit-text-shadow: 0px 1px #FFFFFF;
-o-text-shadow: 0px 1px #FFFFFF;
text-shadow: 0px 1px #FFFFFF;
text-indent: 0px;
}
.quick-view-tooltip .detail-view .detail-group.actions .action.text:hover .label {
color: #000000;
}
.quick-view-tooltip .detail-view .detail-group.actions .detail-actions {
width: 400px;
height: auto;
background: none;
vertical-align: top;
float: left;
}
.quick-view-tooltip .detail-view .detail-group.actions td.view-all {
position: relative;
left: 0px;
float: left;
width: 394px;
height: 22px;
border-top: 1px solid #808080;
/*+box-shadow:inset 0px 1px #FFFFFF;*/
-moz-box-shadow: inset 0px 1px #FFFFFF;
-webkit-box-shadow: inset 0px 1px #FFFFFF;
-o-box-shadow: inset 0px 1px #FFFFFF;
box-shadow: inset 0px 1px #FFFFFF;
}
.quick-view-tooltip .detail-view .detail-actions a {
background: none;
width: 30px;
}
/*Details page*/
.detail-view {
padding: 0 0 0 14px;
@ -2844,7 +3122,7 @@ table td.actions span {
table td.actions .action span.icon {
background-image: url(../images/sprites.png);
cursor: pointer;
width: 28px;
width: 23px;
height: 21px;
float: left;
}
@ -10018,6 +10296,23 @@ div.ui-dialog div.autoscaler div.field-group div.form-container form div.form-it
top: 31px;
}
/*List state BG colors*/
.list-view .body td.item-state-on {
background: #C0FFC0;
border-bottom: 1px solid #09BC09;
}
.list-view .body td.item-state-off {
background: #FFD8CF;
border-bottom: 1px solid #FF9F9F;
}
.list-view .body tr.selected td.item-state-on,
.list-view .body tr.selected td.item-state-off {
background-color: inherit;
border-color: inherit;
}
/*Action icons*/
.action.edit .icon {
background-position: 1px -1px;

View File

@ -25,6 +25,12 @@ under the License.
<% long now = System.currentTimeMillis(); %>
<script language="javascript">
dictionary = {
'label.migrate.to.host': '<fmt:message key="label.migrate.to.host"/>',
'label.migrate.to.storage': '<fmt:message key="label.migrate.to.storage"/>',
'label.stop': '<fmt:message key="label.stop"/>',
'label.reboot': '<fmt:message key="label.reboot"/>',
'label.destroy': '<fmt:message key="label.destroy"/>',
'label.restore': '<fmt:message key="label.restore"/>',
'label.broadcast.uri': '<fmt:message key="label.broadcast.uri"/>',
'label.isolation.uri': '<fmt:message key="label.isolation.uri"/>',
'message.zoneWizard.enable.local.storage': '<fmt:message key="message.zoneWizard.enable.local.storage"/>',

View File

@ -261,6 +261,7 @@
actions: {
edit: {
label: 'message.edit.account',
compactLabel: 'label.edit',
action: function(args) {
var errorMsg = "";
var accountObj = args.context.accounts[0];

View File

@ -152,193 +152,6 @@
notification: {
poll: pollAsyncJobResult
}
},
start: {
label: 'label.action.start.instance' ,
action: function(args) {
$.ajax({
url: createURL("startVirtualMachine&id=" + args.context.instances[0].id),
dataType: "json",
async: true,
success: function(json) {
var jid = json.startvirtualmachineresponse.jobid;
args.response.success(
{_custom:
{jobId: jid,
getUpdatedItem: function(json) {
return json.queryasyncjobresultresponse.jobresult.virtualmachine;
},
getActionFilter: function() {
return vmActionfilter;
}
}
}
);
}
});
},
messages: {
confirm: function(args) {
return 'message.action.start.instance';
},
notification: function(args) {
return 'label.action.start.instance';
},
complete: function(args) {
if(args.password != null) {
alert('Password of the VM is ' + args.password);
}
return 'label.action.start.instance';
}
},
notification: {
poll: pollAsyncJobResult
}
},
stop: {
label: 'label.action.stop.instance',
addRow: 'false',
createForm: {
title: 'label.action.stop.instance',
desc: 'message.action.stop.instance',
fields: {
forced: {
label: 'force.stop',
isBoolean: true,
isChecked: false
}
}
},
action: function(args) {
var array1 = [];
array1.push("&forced=" + (args.data.forced == "on"));
$.ajax({
url: createURL("stopVirtualMachine&id=" + args.context.instances[0].id + array1.join("")),
dataType: "json",
async: true,
success: function(json) {
var jid = json.stopvirtualmachineresponse.jobid;
args.response.success(
{_custom:
{jobId: jid,
getUpdatedItem: function(json) {
return json.queryasyncjobresultresponse.jobresult.virtualmachine;
},
getActionFilter: function() {
return vmActionfilter;
}
}
}
);
}
});
},
messages: {
confirm: function(args) {
return 'message.action.stop.instance';
},
notification: function(args) {
return 'label.action.stop.instance';
}
},
notification: {
poll: pollAsyncJobResult
}
},
restart: {
label: 'instances.actions.reboot.label',
action: function(args) {
$.ajax({
url: createURL("rebootVirtualMachine&id=" + args.context.instances[0].id),
dataType: "json",
async: true,
success: function(json) {
var jid = json.rebootvirtualmachineresponse.jobid;
args.response.success(
{_custom:
{jobId: jid,
getUpdatedItem: function(json) {
return json.queryasyncjobresultresponse.jobresult.virtualmachine;
},
getActionFilter: function() {
return vmActionfilter;
}
}
}
);
}
});
},
messages: {
confirm: function(args) {
return 'message.action.reboot.instance';
},
notification: function(args) {
return 'instances.actions.reboot.label';
}
},
notification: {
poll: pollAsyncJobResult
}
},
destroy: {
label: 'label.action.destroy.instance',
messages: {
confirm: function(args) {
return 'message.action.destroy.instance';
},
notification: function(args) {
return 'label.action.destroy.instance';
}
},
action: function(args) {
$.ajax({
url: createURL("destroyVirtualMachine&id=" + args.context.instances[0].id),
dataType: "json",
async: true,
success: function(json) {
var jid = json.destroyvirtualmachineresponse.jobid;
args.response.success(
{_custom:
{jobId: jid,
getUpdatedItem: function(json) {
return json.queryasyncjobresultresponse.jobresult.virtualmachine;
},
getActionFilter: function() {
return vmActionfilter;
}
}
}
);
}
});
},
notification: {
poll: pollAsyncJobResult
}
},
restore: {
label: 'label.action.restore.instance',
messages: {
confirm: function(args) {
return 'message.action.restore.instance';
},
notification: function(args) {
return 'label.action.restore.instance';
}
},
action: function(args) {
$.ajax({
url: createURL("recoverVirtualMachine&id=" + args.context.instances[0].id),
dataType: "json",
async: true,
success: function(json) {
var item = json.recovervirtualmachineresponse.virtualmachine;
args.response.success({data:item});
}
});
}
}
},
@ -487,6 +300,7 @@
},
stop: {
label: 'label.action.stop.instance',
compactLabel: 'label.stop',
createForm: {
title: 'Stop instance',
desc: 'message.action.stop.instance',
@ -536,6 +350,7 @@
},
restart: {
label: 'label.action.reboot.instance',
compactLabel: 'label.reboot',
action: function(args) {
$.ajax({
url: createURL("rebootVirtualMachine&id=" + args.context.instances[0].id),
@ -572,6 +387,7 @@
},
destroy: {
label: 'label.action.destroy.instance',
compactLabel: 'label.destroy',
messages: {
confirm: function(args) {
return 'message.action.destroy.instance';
@ -608,6 +424,7 @@
},
restore: {
label: 'label.action.restore.instance',
compactLabel: 'label.restore',
messages: {
confirm: function(args) {
return 'message.action.restore.instance';
@ -635,7 +452,7 @@
},
edit: {
label: 'Edit',
label: 'label.edit',
action: function(args) {
var array1 = [];
if(args.data.displayname != args.context.instances[0].displayname)
@ -982,6 +799,7 @@
migrate: {
label: 'label.migrate.instance.to.host',
compactLabel: 'label.migrate.to.host',
messages: {
confirm: function(args) {
return 'message.migrate.instance.to.host';
@ -1060,6 +878,7 @@
migrateToAnotherStorage: {
label: 'label.migrate.instance.to.ps',
compactLabel: 'label.migrate.to.storage',
messages: {
confirm: function(args) {
return 'message.migrate.instance.to.ps';

View File

@ -3880,7 +3880,7 @@
name: 'label.details',
actions: {
configureVpc: {
label: 'label.edit.vpc',
label: 'label.configure',
textLabel: 'label.configure',
action: {
custom: cloudStack.uiCustom.vpc(cloudStack.vpc)

View File

@ -3828,6 +3828,34 @@
}
}
},
dataProvider: function(args) {
var array1 = [];
if(args.filterBy != null) {
if(args.filterBy.search != null && args.filterBy.search.by != null && args.filterBy.search.value != null) {
switch(args.filterBy.search.by) {
case "name":
if(args.filterBy.search.value.length > 0)
array1.push("&keyword=" + args.filterBy.search.value);
break;
}
}
}
$.ajax({
url: createURL("listZones&page=" + args.page + "&pagesize=" + pageSize + array1.join("")),
dataType: "json",
async: true,
success: function(json) {
zoneObjs = json.listzonesresponse.zone;
args.response.success({
actionFilter: zoneActionfilter,
data:zoneObjs
});
}
});
},
actions: {
add: {
label: 'label.add.zone',
@ -3912,127 +3940,98 @@
}
});
}
},
enable: {
label: 'label.action.enable.zone',
messages: {
confirm: function(args) {
return 'message.action.enable.zone';
},
notification: function(args) {
return 'label.action.enable.zone';
}
},
action: function(args) {
$.ajax({
url: createURL("updateZone&id=" + args.context.physicalResources[0].id + "&allocationstate=Enabled"), //embedded objects in listView is called physicalResources while embedded objects in detailView is called zones
dataType: "json",
async: true,
success: function(json) {
var item = json.updatezoneresponse.zone;
args.response.success({
actionFilter: zoneActionfilter,
data:item
});
}
});
},
notification: {
poll: function(args) {
args.complete();
}
}
},
disable: {
label: 'label.action.disable.zone',
messages: {
confirm: function(args) {
return 'message.action.disable.zone';
},
notification: function(args) {
return 'label.action.disable.zone';
}
},
action: function(args) {
$.ajax({
url: createURL("updateZone&id=" + args.context.physicalResources[0].id + "&allocationstate=Disabled"), //embedded objects in listView is called physicalResources while embedded objects in detailView is called zones
dataType: "json",
async: true,
success: function(json) {
var item = json.updatezoneresponse.zone;
args.response.success({
actionFilter: zoneActionfilter,
data:item
});
}
});
},
notification: {
poll: function(args) {
args.complete();
}
}
},
'remove': {
label: 'label.action.delete.zone',
messages: {
confirm: function(args) {
return 'message.action.delete.zone';
},
notification: function(args) {
return 'label.action.delete.zone';
}
},
action: function(args) {
$.ajax({
url: createURL("deleteZone&id=" + args.context.physicalResources[0].id), //embedded objects in listView is called physicalResources while embedded objects in detailView is called zones
dataType: "json",
async: true,
success: function(json) {
args.response.success({data:{}});
}
});
},
notification: {
poll: function(args) { args.complete(); }
}
}
},
dataProvider: function(args) {
var array1 = [];
if(args.filterBy != null) {
if(args.filterBy.search != null && args.filterBy.search.by != null && args.filterBy.search.value != null) {
switch(args.filterBy.search.by) {
case "name":
if(args.filterBy.search.value.length > 0)
array1.push("&keyword=" + args.filterBy.search.value);
break;
}
}
}
$.ajax({
url: createURL("listZones&page=" + args.page + "&pagesize=" + pageSize + array1.join("")),
dataType: "json",
async: true,
success: function(json) {
zoneObjs = json.listzonesresponse.zone;
args.response.success({
actionFilter: zoneActionfilter,
data:zoneObjs
});
}
});
},
detailView: {
isMaximized: true,
actions: {
enable: {
label: 'label.action.enable.zone',
messages: {
confirm: function(args) {
return 'message.action.enable.zone';
},
notification: function(args) {
return 'label.action.enable.zone';
}
},
action: function(args) {
$.ajax({
url: createURL("updateZone&id=" + args.context.physicalResources[0].id + "&allocationstate=Enabled"), //embedded objects in listView is called physicalResources while embedded objects in detailView is called zones
dataType: "json",
async: true,
success: function(json) {
var item = json.updatezoneresponse.zone;
args.response.success({
actionFilter: zoneActionfilter,
data:item
});
}
});
},
notification: {
poll: function(args) {
args.complete();
}
}
},
disable: {
label: 'label.action.disable.zone',
messages: {
confirm: function(args) {
return 'message.action.disable.zone';
},
notification: function(args) {
return 'label.action.disable.zone';
}
},
action: function(args) {
$.ajax({
url: createURL("updateZone&id=" + args.context.physicalResources[0].id + "&allocationstate=Disabled"), //embedded objects in listView is called physicalResources while embedded objects in detailView is called zones
dataType: "json",
async: true,
success: function(json) {
var item = json.updatezoneresponse.zone;
args.response.success({
actionFilter: zoneActionfilter,
data:item
});
}
});
},
notification: {
poll: function(args) {
args.complete();
}
}
},
'remove': {
label: 'label.action.delete.zone',
messages: {
confirm: function(args) {
return 'message.action.delete.zone';
},
notification: function(args) {
return 'label.action.delete.zone';
}
},
action: function(args) {
$.ajax({
url: createURL("deleteZone&id=" + args.context.physicalResources[0].id), //embedded objects in listView is called physicalResources while embedded objects in detailView is called zones
dataType: "json",
async: true,
success: function(json) {
args.response.success({data:{}});
}
});
},
notification: {
poll: function(args) { args.complete(); }
}
},
edit: {
label: 'label.edit',
action: function(args) {
@ -4106,7 +4105,10 @@
},
success: function(json) {
selectedZoneObj = json.listzonesresponse.zone[0];
args.response.success({ data: json.listzonesresponse.zone[0] });
args.response.success({
data: json.listzonesresponse.zone[0],
actionFilter: zoneActionfilter
});
}
});
}
@ -4187,6 +4189,7 @@
},
detailView: {
noCompact: true,
name: 'System VM details',
actions: {
start: {

View File

@ -307,6 +307,7 @@
args.response.success({ actionFilter: actionFilter, data: data });
},
detailView: {
noCompact: true,
tabs: {
network: {
title: 'Network',

View File

@ -21,6 +21,9 @@
if (!$row) return;
var $listView = $row.closest('.list-view');
if (!$listView.parents('html').size()) return;
var $newRow;
var jsonObj = $row.data('json-obj');
@ -94,6 +97,7 @@
if (!options) options = {};
var $form = options.$form;
var viewArgs = $detailView.data('view-args');
if (customAction && !noAdd) {
customAction({
@ -119,7 +123,7 @@
// Success
function(args) {
if (!$detailView.is(':visible')) return;
if (!$detailView.parents('html').size()) return;
$loading.remove();
replaceListViewItem($detailView, args.data);
@ -162,10 +166,10 @@
cloudStack.ui.notifications.add(
notification,
function(args2) { //name parameter as "args2" instead of "args" to avoid override "args" from success: function(args) {
if ($detailView.is(':visible')) {
if ($detailView.parents('html').size()) {
$loading.remove();
if (!noRefresh) {
if (!noRefresh && !viewArgs.compact) {
updateTabContent(args.data? args.data : args2.data);
}
}
@ -180,6 +184,10 @@
}));
replaceListViewItem($detailView, args.data ? args.data : args2.data);
if (viewArgs && viewArgs.onActionComplete) {
viewArgs.onActionComplete();
}
},
{},
@ -202,6 +210,10 @@
}
}
});
if (viewArgs && viewArgs.onPerformAction) {
viewArgs.onPerformAction();
}
}
};
@ -646,11 +658,16 @@
});
$.each(actions, function(key, value) {
if ($.inArray(key, allowedActions) == -1) return true;
if ($.inArray(key, allowedActions) == -1 ||
(key == 'edit' && options.compact)) return true;
var $action = $('<div></div>')
.addClass('action').addClass(key)
.appendTo($actions.find('div.buttons'));
.appendTo($actions.find('div.buttons'))
.attr({
title: _l(value.label),
alt: _l(value.label)
});
var $actionLink = $('<a></a>')
.attr({
href: '#',
@ -664,11 +681,17 @@
)
.appendTo($action);
if (value.textLabel) {
if (value.textLabel || options.compact) {
$action
.addClass('single text')
.prepend(
$('<span>').addClass('label').html(_l(value.textLabel))
$('<span>').addClass('label').html(
_l(
options.compact ?
(value.compactLabel ?
value.compactLabel : value.label) : value.textLabel
)
)
);
}
@ -835,8 +858,9 @@
$actions = makeActionButtons(detailViewArgs.actions, {
actionFilter: actionFilter,
data: data,
context: $detailView.data('view-args').context
});
context: $detailView.data('view-args').context,
compact: detailViewArgs.compact
}).prependTo($firstRow.closest('div.detail-group').closest('.details'));
// 'View all' button
var showViewAll = detailViewArgs.viewAll ?
@ -912,9 +936,11 @@
{ activeTab: targetTabID }
);
$tabContent.append(
$('<div>').addClass('loading-overlay')
);
if (!$detailView.data('view-args').compact) {
$tabContent.append(
$('<div>').addClass('loading-overlay')
);
}
return dataProvider({
tab: targetTabID,
@ -968,6 +994,10 @@
).appendTo($detailView.find('.main-groups'));
}
if ($detailView.data('view-args').onLoad) {
$detailView.data('view-args').onLoad($detailView);
}
return true;
},
error: function() {
@ -986,6 +1016,8 @@
var tabFilter = options.tabFilter;
var context = options.context ? options.context : {};
var updateContext = $detailView.data('view-args').updateContext;
var compact = options.compact;
var tabTotal = 0;
if (updateContext) {
$.extend($detailView.data('view-args').context, updateContext({
@ -1010,6 +1042,7 @@
$.each(tabs, function(key, value) {
// Don't render tab, if filtered out
if ($.inArray(key, removedTabs) > -1) return true;
if (compact && tabTotal) return true;
var propGroup = key;
var prop = value;
@ -1027,6 +1060,8 @@
$tabContent.data('detail-view-tab-id', key);
$tabContent.data('detail-view-tab-data', value);
tabTotal++;
return true;
});
@ -1059,9 +1094,12 @@
$.fn.detailView = function(args, options) {
var $detailView = this;
var compact = args.compact;
var $toolbar = makeToolbar();
var $tabs;
if (options == 'refresh') {
var $tabs = replaceTabs($detailView, args.tabs, {
$tabs = replaceTabs($detailView, args.tabs, {
context: args.context,
tabFilter: args.tabFilter
});
@ -1073,18 +1111,22 @@
$detailView.data('list-view-row', args.$listViewRow);
}
// Create toolbar
var $toolbar = makeToolbar().appendTo($detailView);
// Create tabs
var $tabs = makeTabs($detailView, args.tabs, {
$tabs = makeTabs($detailView, args.tabs, {
compact: compact,
context: args.context,
tabFilter: args.tabFilter
}).appendTo($detailView);
});
$tabs.appendTo($detailView);
// Create toolbar
if (!compact) {
$toolbar.appendTo($detailView);
}
}
$detailView.tabs();
return $detailView;
};

View File

@ -614,9 +614,12 @@
var $thead = $('<thead>').prependTo($table).append($('<tr>'));
var reorder = options.reorder;
var detailView = options.detailView;
var viewArgs = $table.closest('.list-view').data('view-args');
var uiCustom = viewArgs.uiCustom;
var hiddenFields = [];
if(preFilter != null)
if (preFilter != null)
hiddenFields = preFilter();
$.each(fields, function(key) {
@ -628,14 +631,18 @@
if ($th.index()) $th.addClass('reduced-hide');
$th.html(_l(field.label));
return true;
});
// Re-order row buttons
if (reorder) {
$thead.find('tr').append(
$('<th>').html(_l('label.order')).addClass('reorder-actions reduced-hide')
);
}
// Actions column
if (actions && renderActionCol(actions)) {
$thead.find('tr').append(
$('<th></th>')
@ -644,6 +651,15 @@
);
}
// Quick view
if (detailView && !detailView.noCompact && !uiCustom) {
$thead.find('tr').append(
$('<th></th>')
.html('Quickview')
.addClass('quick-view reduced-hide')
);
}
return $thead;
};
@ -665,6 +681,8 @@
}).html(_l(this.label));
$option.appendTo($filterSelect);
return true;
});
return $filters.appendTo($toolbar);
@ -765,7 +783,7 @@
/**
* Initialize detail view for specific ID from list view
*/
var createDetailView = function(args, complete, $row) {
var createDetailView = function(args, complete, $row, options) {
var $panel = args.$panel;
var title = args.title;
var id = args.id;
@ -775,9 +793,11 @@
jsonObj: args.jsonObj,
section: args.section,
context: args.context,
$listViewRow: $row
$listViewRow: $row,
compact: options ? options.compact : false
});
var noPanel = options ? options.noPanel : false;
var $detailView, $detailsPanel;
var panelArgs = {
title: title,
@ -798,14 +818,19 @@
}
};
// Create panel
$detailsPanel = data.$browser.cloudBrowser('addPanel', panelArgs);
if (noPanel) {
return $('<div>').detailView(data);
} else {
$detailsPanel = data.$browser.cloudBrowser('addPanel', panelArgs);
}
};
var addTableRows = function(preFilter, fields, data, $tbody, actions, options) {
if (!options) options = {};
var rows = [];
var reorder = options.reorder;
var detailView = options.detailView;
var uiCustom = $tbody.closest('.list-view').data('view-args').uiCustom;
if (!data || ($.isArray(data) && !data.length)) {
if (!$tbody.find('tr').size()) {
@ -824,6 +849,7 @@
$(data).each(function() {
var dataItem = this;
var id = dataItem.id;
var $quickView;
var $tr = $('<tr>');
rows.push($tr);
@ -851,7 +877,11 @@
if (field.indicator) {
$td.addClass('state').addClass(field.indicator[content]);
// Disabling indicator for now per new design
//$tr.find('td:first').addClass('item-state-' + field.indicator[content]);
}
if (field.id == true) id = field.id;
if ($td.index()) $td.addClass('reduced-hide');
if (field.action) {
@ -992,6 +1022,95 @@
}
);
}
// Add quick view
if (detailView && !detailView.noCompact && !uiCustom) {
$quickView = $('<td>').addClass('quick-view reduced-hide')
.append(
$('<span>').addClass('icon').html('&nbsp;')
)
.appendTo($tr);
$quickView.mouseover(
// Show quick view
function() {
var $quickViewTooltip = $('<div>').addClass('quick-view-tooltip');
var $tr = $quickView.closest('tr');
var $listView = $tr.closest('.list-view');
var $title = $('<div>').addClass('title');
var $detailsContainer = $('<div>').addClass('container').appendTo($quickViewTooltip);
var context = $.extend(true, {}, options.context);
var activeSection = $listView.data('view-args').activeSection;
var itemID = $tr.data('list-view-item-id');
var jsonObj = $tr.data('json-obj');
var $loading = $('<div>').addClass('loading-overlay').appendTo($detailsContainer);
if ($tr.hasClass('loading')) return;
// Title
$title.append(
$('<span>').html('Quickview: '),
$('<span>').addClass('title').html(
cloudStack.concat(
$tr.find('td:first span').html(), 30
)
),
$('<span>').addClass('icon').html('&nbsp;')
);
$quickViewTooltip.append($title);
// Setup positioning
$quickViewTooltip.hide().appendTo('#container').fadeIn(200, function() {
if (!$quickViewTooltip.is(':visible')) return;
// Init detail view
context[activeSection] = [jsonObj];
createDetailView(
{
data: $.extend(true, {}, detailView, {
onLoad: function($detailView) {
$loading.remove();
$detailView.slideToggle('fast');
},
onPerformAction: function() {
$tr.addClass('loading').find('td:last').prepend($('<div>').addClass('loading'));
$quickViewTooltip.hide();
},
onActionComplete: function() {
$tr.removeClass('loading').find('td:last .loading').remove();
$quickViewTooltip.remove();
}
}),
id: itemID,
jsonObj: jsonObj,
section: activeSection,
context: context,
$listViewRow: $tr
},
function($detailView) { //complete(), callback funcion
$detailView.data('list-view', $listView);
}, $tr,
{
compact: true,
noPanel: true
}
).appendTo($detailsContainer).hide();
});
$quickViewTooltip.css({
position: 'absolute',
left: $tr.width() + ($quickViewTooltip.width() -
($quickViewTooltip.width() / 2)),
top: $quickView.offset().top - 50,
zIndex: $tr.closest('.panel').zIndex() + 1
});
$quickViewTooltip.mouseleave(function() {
if (!$('.overlay:visible').size()) {
$quickViewTooltip.remove();
}
});
}
);
}
});
return rows;
@ -1051,7 +1170,8 @@
addTableRows(preFilter, fields, args.data, $tbody, actions, {
actionFilter: args.actionFilter,
context: context,
reorder: reorder
reorder: reorder,
detailView: options.detailView
});
$table.dataTable(null, { noSelect: uiCustom });
@ -1272,7 +1392,10 @@
listViewData.fields,
$table,
listViewData.actions,
{ reorder: reorder });
{
reorder: reorder,
detailView: listViewData.detailView
});
createFilters($toolbar, listViewData.filters);
createSearchBar($toolbar, listViewData);
@ -1296,7 +1419,8 @@
listViewData.actions,
{
context: args.context,
reorder: reorder
reorder: reorder,
detailView: listViewData.detailView
}
);
@ -1348,7 +1472,8 @@
listViewData.actions,
{
context: $listView.data('view-args').context,
reorder: listViewData.reorder
reorder: listViewData.reorder,
detailView: listViewData.detailView
}
);
};
@ -1397,7 +1522,8 @@
listViewData.actions,
{
context: $listView.data('view-args').context,
reorder: listViewData.reorder
reorder: listViewData.reorder,
detailView: listViewData.detailView
}
);
};
@ -1443,12 +1569,17 @@
filterBy.advSearch = $listView.data('advSearch');
}
loadBody($table, listViewData.dataProvider, listViewData.preFilter, listViewData.fields, true, {
loadBody(
$table,
listViewData.dataProvider,
listViewData.preFilter,
listViewData.fields, true, {
context: context,
page: page,
filterBy: filterBy
}, actions, {
reorder: listViewData.reorder
reorder: listViewData.reorder,
detailView: listViewData.detailView
});
}
}, 500);
@ -1608,7 +1739,8 @@
{
prepend: true,
actionFilter: actionFilter,
reorder: reorder
reorder: reorder,
detailView: targetArgs.detailView
}
)[0];
listView.find('table').dataTable('refresh');
@ -1639,7 +1771,8 @@
targetArgs.actions,
{
actionFilter: actionFilter ? actionFilter : defaultActionFilter,
reorder: reorder
reorder: reorder,
detailView: targetArgs.detailView
}
)[0];
@ -1680,7 +1813,8 @@
null,
listViewArgs.actions,
{
context: this.data('view-args').context
context: this.data('view-args').context,
detailView: listViewArgs.detailView
}
);
} else {