You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by al...@apache.org on 2012/07/24 01:37:40 UTC
[8/14] git commit: Implement tag UI widget
Implement tag UI widget
Create UI for handling new tag API. This currently supports the detail view and multi-edit
To enable tags UI, add a 'tags' object to each detailView/multiEdit configuration:
tabs: {
...
details: {
...
tags: {
actions: {
add: function(args) {
setTimeout(function() {
args.response.success({
notification: {
desc: 'Add tags for instance',
poll: testData.notifications.testPoll
}
});
}, 500);
},
remove: function(args) {
args.response.success({
notification: {
desc: 'Remove tags for instance',
poll: testData.notifications.testPoll
}
});
}
},
dataProvider: function(args) {
args.response.success({
data: [
{
id: '1',
key: 'user',
value: 'brian'
},
{
id: '2',
key: 'region',
value: 'usa'
}
]
});
}
}
...
Conflicts:
ui/css/cloudstack3.css
ui/scripts/ui/widgets/tagger.js
Project: http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/commit/5e94b0d1
Tree: http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/tree/5e94b0d1
Diff: http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/diff/5e94b0d1
Branch: refs/heads/vpc
Commit: 5e94b0d12e50c6ba3a7defd00aeb04c79286f14f
Parents: 88f7872
Author: bfederle <bf...@gmail.com>
Authored: Mon Jul 23 13:26:52 2012 -0700
Committer: Brian Federle <br...@citrix.com>
Committed: Mon Jul 23 15:18:36 2012 -0700
----------------------------------------------------------------------
ui/css/cloudstack3.css | 198 ++++++++++++++++++++++++++++++
ui/scripts/ui/widgets/detailView.js | 48 ++++++-
ui/scripts/ui/widgets/multiEdit.js | 12 ++-
ui/scripts/ui/widgets/tagger.js | 197 +++++++++++++++++++++++++++++
4 files changed, 446 insertions(+), 9 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/5e94b0d1/ui/css/cloudstack3.css
----------------------------------------------------------------------
diff --git a/ui/css/cloudstack3.css b/ui/css/cloudstack3.css
index ef81697..e405fbf 100644
--- a/ui/css/cloudstack3.css
+++ b/ui/css/cloudstack3.css
@@ -8952,6 +8952,204 @@ div.panel.ui-dialog div.list-view div.fixed-header {
background: #DFE1E3;
}
+/*Tagger*/
+.tagger {
+ width: 94%;
+ margin: auto;
+ padding-bottom: 12px;
+ background: #F2F0F0;
+ border: 1px solid #CFC9C9;
+ /*+placement:shift -4px 0px;*/
+ position: relative;
+ left: -4px;
+ top: 0px;
+}
+
+.tagger .field {
+ width: 179px;
+ float: left;
+ position: relative;
+}
+
+.tagger .tag-info {
+ font-size: 11px;
+ color: #757575;
+ margin-top: 12px;
+ margin-left: 8px;
+}
+
+.tagger .tag-info.title {
+ font-size: 11px;
+ color: #6F9BF0;
+ margin-bottom: 5px;
+}
+
+.tagger form {
+ margin: 12px 9px 0px;
+}
+
+.tagger.readonly form {
+ display: none;
+}
+
+.tagger form label {
+ display: block;
+ float: left;
+ width: 28px;
+ text-align: right;
+ font-size: 10px;
+ color: #394552;
+ margin-right: 9px;
+ /*+placement:shift 5px 8px;*/
+ position: relative;
+ left: 5px;
+ top: 8px;
+}
+
+.tagger form label.error {
+ position: absolute;
+ color: #FF0000;
+ left: 42px;
+ top: 29px;
+ /*[empty]background-color:;*/
+}
+
+.tagger form input {
+ padding: 4px;
+ background: #FFFFFF;
+ border: 1px solid #808080;
+ /*+border-radius:4px;*/
+ -moz-border-radius: 4px;
+ -webkit-border-radius: 4px;
+ -khtml-border-radius: 4px;
+ border-radius: 4px;
+}
+
+.tagger form input[type=submit] {
+ background: url(../images/bg-gradients.png) repeat-x 0px -220px;
+ cursor: pointer;
+ color: #FFFFFF;
+ /*+text-shadow:0px -1px 2px #000000;*/
+ -moz-text-shadow: 0px -1px 2px #000000;
+ -webkit-text-shadow: 0px -1px 2px #000000;
+ -o-text-shadow: 0px -1px 2px #000000;
+ text-shadow: 0px -1px 2px #000000;
+ border: none;
+ /*+border-radius:4px;*/
+ -moz-border-radius: 4px;
+ -webkit-border-radius: 4px;
+ -khtml-border-radius: 4px;
+ border-radius: 4px;
+ padding: 7px 25px 7px 26px;
+ margin-left: 16px;
+}
+
+.tagger form input[type=submit]:hover {
+ background-position: 0px -946px;
+}
+
+.tagger ul {
+ display: block;
+ width: 96%;
+ margin: 16px auto auto;
+ /*+border-radius:2px;*/
+ -moz-border-radius: 2px;
+ -webkit-border-radius: 2px;
+ -khtml-border-radius: 2px;
+ border-radius: 2px;
+ overflow: auto;
+ padding-bottom: 10px;
+ border: 1px solid #D2D2D2;
+ background: #FFFFFF;
+ /*+box-shadow:inset 0px 0px 10px #DCDCDC;*/
+ -moz-box-shadow: inset 0px 0px 10px #DCDCDC;
+ -webkit-box-shadow: inset 0px 0px 10px #DCDCDC;
+ -o-box-shadow: inset 0px 0px 10px #DCDCDC;
+ box-shadow: inset 0px 0px 10px #DCDCDC;
+}
+
+.tagger.readonly ul {
+}
+
+.tagger ul li {
+ background: #DFDFDF 0px 4px;
+ height: 15px;
+ padding: 0px 18px 0 7px;
+ display: inline-block;
+ float: left;
+ margin-left: 7px;
+ margin-right: 2px;
+ margin-top: 5px;
+ /*+border-radius:4px;*/
+ -moz-border-radius: 4px;
+ -webkit-border-radius: 4px;
+ -khtml-border-radius: 4px;
+ border-radius: 4px;
+ /*+placement:shift 0px 2px;*/
+ position: relative;
+ left: 0px;
+ top: 2px;
+}
+
+.tagger ul li span {
+ color: #000000;
+}
+
+.tagger ul li span.label {
+ font-size: 10px;
+ position: relative;
+ left: 15px;
+ top: -2px;
+}
+
+.tagger.readonly ul li span.label {
+ left: 6px;
+}
+
+.tagger ul li span.remove {
+ width: 15px !important;
+ overflow: hidden !important;
+ height: 11px !important;
+ background: #DFDFDF url(../images/sprites.png) no-repeat -596px -1183px;
+ display: block;
+ top: 0px !important;
+ left: -3px !important;
+ text-indent: 4px;
+ padding: 4px 0px 0px 8px;
+ font-size: 8px;
+ font-weight: bold;
+ cursor: pointer;
+ position: absolute !important;
+ color: #5B5B5B;
+}
+
+.tagger.readonly ul li span.remove {
+ display: none;
+}
+
+.tagger ul li span.remove:hover {
+ color: #000000;
+}
+
+/** Dialog tagger*/
+.ui-dialog .tagger {
+}
+
+.ui-dialog .tagger .field {
+ width: 119px !important;
+}
+
+.ui-dialog .tagger input.key,
+.ui-dialog .tagger input.value {
+ width: 66px !important;
+ height: 15px;
+ font-size: 11px !important;
+}
+
+.ui-dialog .tagger input[type=submit] {
+ padding: 6px 15px;
+}
+
/*VPC / vApps*/
.vpc-chart {
width: 100%;
http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/5e94b0d1/ui/scripts/ui/widgets/detailView.js
----------------------------------------------------------------------
diff --git a/ui/scripts/ui/widgets/detailView.js b/ui/scripts/ui/widgets/detailView.js
index b1e9a68..086f66d 100644
--- a/ui/scripts/ui/widgets/detailView.js
+++ b/ui/scripts/ui/widgets/detailView.js
@@ -287,10 +287,14 @@
* @param callback
*/
edit: function($detailView, args) {
+ $detailView.addClass('edit-mode');
+
if ($detailView.find('.button.done').size()) return false;
// Convert value TDs
- var $inputs = $detailView.find('input, select');
+ var $inputs = $detailView.find('input, select').filter(function() {
+ return !$(this).closest('.tagger').size() && !$(this).attr('type') == 'submit';
+ });
var action = args.actions[args.actionName];
var id = $detailView.data('view-args').id;
var $editButton = $('<div>').addClass('button done').html(_l('label.apply')).hide();
@@ -302,9 +306,14 @@
$detailView.find('.ui-tabs-panel .detail-group.actions')
).fadeIn();
+ $detailView.find('.tagger').removeClass('readonly');
+ $detailView.find('.tagger').find('input[type=text]').val('');
+
var convertInputs = function($inputs) {
// Save and turn back into labels
$inputs.each(function() {
+ if ($(this).closest('.tagger').size()) return true;
+
var $input = $(this);
var $value = $input.closest('td.value span');
@@ -328,8 +337,12 @@
};
var removeEditForm = function() {
+ $detailView.removeClass('edit-mode');
+
// Remove Edit form
- var $form = $detailView.find('form');
+ var $form = $detailView.find('form').filter(function() {
+ return !$(this).closest('.tagger').size();
+ });
if ($form.size()) {
var $mainGroups = $form.find('div.main-groups').detach();
$form.parent('div').append($mainGroups);
@@ -337,11 +350,15 @@
}
//Remove required labels
$detailView.find('span.field-required').remove();
- }
+ $detailView.find('.tagger').addClass('readonly');
+
+ };
// Put in original values
var cancelEdits = function($inputs, $editButton) {
$inputs.each(function() {
+ if ($(this).closest('.tagger').size()) return true;
+
var $input = $(this);
var $value = $input.closest('td.value span');
var originalValue = $input.data('original-value');
@@ -424,8 +441,12 @@
};
$editButton.click(function() {
- var $inputs = $detailView.find('input, select'),
- $form = $detailView.find('form');
+ var $inputs = $detailView.find('input, select').filter(function() {
+ return !$(this).closest('.tagger').size();
+ });
+ var $form = $detailView.find('form').filter(function() {
+ return !$(this).closest('.tagger').size();
+ });
if ($(this).hasClass('done')) {
if (!$form.valid()) {
@@ -438,6 +459,8 @@
} else { // Cancel
cancelEdits($inputs, $editButton);
}
+
+ return true;
});
$detailView.find('td.value span').each(function() {
@@ -511,8 +534,11 @@
}
// Setup form validation
- $detailView.find('form').validate();
- $detailView.find('form').find('input, select').each(function() {
+ var $form = $detailView.find('form').filter(function() {
+ return !$(this).closest('.tagger').size();
+ });
+ $form.validate();
+ $form.find('input, select').each(function() {
var data = $(this).parent('span').data('validation-rules');
if (data) {
$(this).rules('add', data);
@@ -934,6 +960,14 @@
actionFilter: actionFilter
}).appendTo($tabContent);
+ if (tabs.tags) {
+ $('<div>').tagger(
+ $.extend(true, {}, tabs.tags, {
+ context: $detailView.data('view-args').context
+ })
+ ).appendTo($tabContent).addClass('readonly');
+ }
+
return true;
},
error: function() {
http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/5e94b0d1/ui/scripts/ui/widgets/multiEdit.js
----------------------------------------------------------------------
diff --git a/ui/scripts/ui/widgets/multiEdit.js b/ui/scripts/ui/widgets/multiEdit.js
index 64d94f6..89d2603 100644
--- a/ui/scripts/ui/widgets/multiEdit.js
+++ b/ui/scripts/ui/widgets/multiEdit.js
@@ -332,10 +332,16 @@
after: function(args) {
var $loading = $('<div>').addClass('loading-overlay').prependTo($dataItem);
performAction({ data: args.data, complete: function() {
- $multi.multiEdit('refresh');
+ $multi.trigger('refresh');
} });
}
});
+
+ if (options.tags) {
+ $(':ui-dialog').append(
+ $('<div>').addClass('multi-edit-tags').tagger(options.tags)
+ );
+ }
}
})
);
@@ -652,6 +658,7 @@
$.fn.multiEdit = function(args) {
var dataProvider = args.dataProvider;
var multipleAdd = args.multipleAdd;
+ var tags = args.tags;
var $multi = $('<div>').addClass('multi-edit').appendTo(this);
var $multiForm = $('<form>').appendTo($multi);
var $inputTable = $('<table>').addClass('multi-edit').appendTo($multiForm);
@@ -918,7 +925,8 @@
context: $.extend(true, {}, context, this._context),
ignoreEmptyFields: ignoreEmptyFields,
preFilter: actionPreFilter,
- listView: listView
+ listView: listView,
+ tags: tags
}
).appendTo($dataBody);
});
http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/5e94b0d1/ui/scripts/ui/widgets/tagger.js
----------------------------------------------------------------------
diff --git a/ui/scripts/ui/widgets/tagger.js b/ui/scripts/ui/widgets/tagger.js
new file mode 100644
index 0000000..b6d2f0a
--- /dev/null
+++ b/ui/scripts/ui/widgets/tagger.js
@@ -0,0 +1,197 @@
+(function($, cloudStack) {
+ var elems = {
+ inputArea: function(args) {
+ var $form = $('<form>').addClass('tag-input');
+ var $keyField = $('<div>').addClass('field key');
+ var $keyLabel = $('<label>').attr('for', 'key').html('Key:');
+ var $key = $('<input>').addClass('key required').attr('name', 'key');
+ var $valueField = $('<div>').addClass('field value');
+ var $valueLabel = $('<label>').attr('for', 'value').html('Value:');
+ var $value = $('<input>').addClass('value required').attr('name', 'value');
+ var $submit = $('<input>').attr('type', 'submit').val('Add');
+
+ $keyField.append($keyLabel, $key);
+ $valueField.append($valueLabel, $value);
+ $form.append(
+ $keyField, $valueField,
+ $submit
+ );
+
+ $form.validate({ onfocusout: false });
+
+ $form.submit(
+ args.onSubmit ?
+ function() {
+ if (!$form.valid()) return false;
+
+ args.onSubmit({
+ data: cloudStack.serializeForm($form),
+ response: {
+ success: function() {
+ // Restore editing of input
+ $key.attr('disabled', false);
+ $value.attr('disabled', false);
+
+ // Clear out old data
+ $key.val(''); $value.val('');
+ $key.focus();
+ },
+ error: function() {
+ // Restore editing of input
+ $key.attr('disabled', false);
+ $value.attr('disabled', false);
+ $key.focus();
+ }
+ }
+ });
+
+ // Prevent input during submission
+ $key.attr('disabled', 'disabled');
+ $value.attr('disabled', 'disabled');
+
+ return false;
+ } :
+ function() { return false; }
+ );
+
+ return $form;
+ },
+ tagItem: function(title, onRemove) {
+ var $li = $('<li>');
+ var $label = $('<span>').addClass('label').html(title);
+ var $remove = $('<span>').addClass('remove').html('X');
+
+ $remove.click(function() {
+ if (onRemove) onRemove($li);
+ });
+
+ $li.append($remove, $label);
+
+ return $li;
+ },
+
+ info: function(text) {
+ var $info = $('<div>').addClass('tag-info');
+ var $text = $('<span>').html(text);
+
+ $text.appendTo($info);
+
+ return $info;
+ }
+ };
+
+ $.widget('cloudStack.tagger', {
+ _init: function(args) {
+ var context = this.options.context;
+ var dataProvider = this.options.dataProvider;
+ var actions = this.options.actions;
+ var $container = this.element.addClass('tagger');
+ var $tagArea = $('<ul>').addClass('tags');
+ var $title = elems.info('Tags').addClass('title');
+ var $loading = $('<div>').addClass('loading-overlay');
+
+ var onRemoveItem = function($item) {
+ $loading.appendTo($container);
+ actions.remove({
+ context: context,
+ response: {
+ success: function(args) {
+ var notification = $.extend(true, {} , args.notification, {
+ interval: 500,
+ _custom: args._custom
+ });
+
+ cloudStack.ui.notifications.add(
+ notification,
+
+ // Success
+ function() {
+ $loading.remove();
+ $item.remove();
+ }, {},
+
+ // Error
+ function() {
+ $loading.remove();
+ }, {}
+ );
+ },
+ error: function(message) {
+ $loading.remove();
+ cloudStack.dialog.notice({ message: message });
+ }
+ }
+ });
+ };
+
+ var $inputArea = elems.inputArea({
+ onSubmit: function(args) {
+ var data = args.data;
+ var success = args.response.success;
+ var error = args.response.error;
+ var title = data.key + ' = ' + data.value;
+
+ $loading.appendTo($container);
+ actions.add({
+ context: context,
+ response: {
+ success: function(args) {
+ var notification = $.extend(true, {} , args.notification, {
+ interval: 500,
+ _custom: args._custom
+ });
+
+ cloudStack.ui.notifications.add(
+ notification,
+
+ // Success
+ function() {
+ $loading.remove();
+ elems.tagItem(title, onRemoveItem).appendTo($tagArea);
+ success();
+ }, {},
+
+ // Error
+ function() {
+ $loading.remove();
+ error();
+ }, {}
+ );
+ },
+ error: function(message) {
+ $loading.remove();
+ error();
+ cloudStack.dialog.notice({ message: message });
+ }
+ }
+ });
+ }
+ });
+
+ $container.append($title, $inputArea, $tagArea);
+
+ // Get data
+ $loading.appendTo($container);
+ dataProvider({
+ context: context,
+ response: {
+ success: function(args) {
+ var data = args.data;
+
+ $loading.remove();
+ $(data).map(function(index, item) {
+ var key = item.key;
+ var value = item.value;
+
+ elems.tagItem(key + ' = ' + value, onRemoveItem).appendTo($tagArea);
+ });
+ },
+ error: function(message) {
+ $loading.remove();
+ $container.find('ul').html(message);
+ }
+ }
+ });
+ }
+ });
+}(jQuery, cloudStack));