From 025980d009badf48f697f69ad6589e42f14bce31 Mon Sep 17 00:00:00 2001 From: chouseknecht Date: Fri, 25 Oct 2013 04:07:07 -0400 Subject: [PATCH] Drag-n-drop feature on custom tree functions graphically. Ready to add API calls. --- awx/ui/static/js/controllers/JobEvents.js | 2 +- awx/ui/static/less/ansible-ui.less | 31 +- awx/ui/static/lib/ansible/TreeSelector.js | 108 +++++-- .../static/lib/ansible/TreeSelector.js.backup | 279 ++++++++++++++++++ awx/ui/static/lib/ansible/form-generator.js | 10 +- 5 files changed, 391 insertions(+), 39 deletions(-) create mode 100644 awx/ui/static/lib/ansible/TreeSelector.js.backup diff --git a/awx/ui/static/js/controllers/JobEvents.js b/awx/ui/static/js/controllers/JobEvents.js index 75d1d42758..82afcc2f2d 100644 --- a/awx/ui/static/js/controllers/JobEvents.js +++ b/awx/ui/static/js/controllers/JobEvents.js @@ -30,7 +30,7 @@ function JobEventsList ($scope, $rootScope, $location, $log, $routeParams, Rest, scope.selected = []; scope.expand = true; //on load, automatically expand all nodes - scope.parentNode = 'parent-event'; // used in ngClass to dynamicall set row level class and control + scope.parentNode = 'parent-event'; // used in ngClass to dynamically set row level class and control scope.childNode = 'child-event'; // link color and cursor if (scope.removeSetHostLinks) { diff --git a/awx/ui/static/less/ansible-ui.less b/awx/ui/static/less/ansible-ui.less index 8d39249232..81c9fa3f2f 100644 --- a/awx/ui/static/less/ansible-ui.less +++ b/awx/ui/static/less/ansible-ui.less @@ -15,6 +15,7 @@ @blue: #1778c3; /* logo blue */ @blue-link: #0088cc; @grey: #A9A9A9; +@well: #f5f5f5; /* well background color */ @green: #5bb75b; @info: #d9edf7; /* alert info background color */ @info-border: #bce8f1; /* alert info border color */ @@ -995,11 +996,10 @@ select.field-mini-height { ul { list-style-type: none; padding-left: 16px; - padding-top: 5px; } - - li { - padding-bottom: 5px; + + li { + padding-top: 3px; } .tree-root { @@ -1019,11 +1019,18 @@ select.field-mini-height { font-weight: bold; } - div { + .expand-container, + .badge-container, + .title-container { display: inline-block; vertical-align: top; } + .badge-container, + .title-container { + border-bottom: 1px solid @well; + } + .expand-container { width: 14px; text-align: center; @@ -1053,6 +1060,14 @@ select.field-mini-height { padding: 10px; } +// drag-n-drop tree styles +.droppable-hover { + background-color: #C0C0C0; + color: @black; + border: 1px solid #808080; + font-weight: normal; +} + .disabled { color: @grey; } @@ -1265,7 +1280,11 @@ tr td button i { /* Large desktop */ @media (min-width: 1200px) { - + + .container { + max-width: 96%; + } + .delete-btn { /* Used on job and project page to make cancel and delete buttons have an equal width */ width: 60px; diff --git a/awx/ui/static/lib/ansible/TreeSelector.js b/awx/ui/static/lib/ansible/TreeSelector.js index fbbeccce19..191a93b64d 100644 --- a/awx/ui/static/lib/ansible/TreeSelector.js +++ b/awx/ui/static/lib/ansible/TreeSelector.js @@ -22,31 +22,6 @@ angular.module('TreeSelector', ['Utilities', 'RestServices']) var toolTip = 'Hosts have failed jobs?'; var idx = 0; - function buildHTML(tree_data) { - var sorted = SortNodes(tree_data); - html += (sorted.length > 0) ? "\n"; - } - function refresh(parent) { var group, title; var id = parent.attr('id'); @@ -155,10 +130,88 @@ angular.module('TreeSelector', ['Utilities', 'RestServices']) $(this).css('width','80%'); } }); + + // Make the tree drag-n-droppable + + $('#selector-tree .activate').draggable({ + cursor: "pointer", + cursorAt: { top: -16, left: -10 }, + revert: 'invalid', + helper: 'clone', + start: function (e, ui) { + var txt = '[ ' + ui.helper.text() + ' ]'; + ui.helper.css({ 'font-weight': 'normal', 'color': '#A9A9A9', 'background-color': '#f5f5f5' }).text(txt); + } + }) + .droppable({ + //hoverClass: 'droppable-hover', + tolerance: 'pointer', + over: function (e, ui) { + var p = $(this).parent().parent(); + p.find('div').each(function(idx) { + if (idx > 0 && idx < 3) { + $(this).css({ 'border-bottom': '1px dotted #808080' }); + } + }); + var c = p.find('.expand-container').first(); + c.empty().html(''); + }, + out: function (e, ui) { + var p = $(this).parent().parent(); + p.find('div').each(function(idx) { + if (idx > 0 && idx < 3) { + $(this).css({ 'border-bottom': '1px solid #f5f5f5' }); + } + }); + var c = p.find('.expand-container'); + var icon; + c.first().empty(); + if (c.length > 1) { + // has children, put expander icon back + icon = (p.attr('data-state') == 'opened') ? 'icon-caret-down' : 'icon-caret-right'; + c.first().html(''); + c.first().find('a').first().bind('click', toggle); + } + } + }) Wait('stop'); }); - + + function buildHTML(tree_data) { + var sorted = SortNodes(tree_data); + html += (sorted.length > 0) ? "\n"; + } + // Build the HTML for our tree if (scope.buildAllGroupsRemove) { scope.buildAllGroupsRemove(); @@ -168,7 +221,7 @@ angular.module('TreeSelector', ['Utilities', 'RestServices']) Rest.get() .success( function(data, status, headers, config) { buildHTML(data); - scope.$emit('searchTreeReady', html + "\n\n"); + scope.$emit('searchTreeReady', html + "\n\n\n"); }) .error( function(data, status, headers, config) { ProcessErrors(scope, data, status, null, @@ -208,6 +261,7 @@ angular.module('TreeSelector', ['Utilities', 'RestServices']) Rest.get() .success( function(data, status, headers, config) { html += "
Group Selector:
\n" + + "
\n" + "\n"); + }) + .error( function(data, status, headers, config) { + ProcessErrors(scope, data, status, null, + { hdr: 'Error!', msg: 'Failed to get inventory tree for: ' + inventory_id + '. GET returned: ' + status }); + }); + }); + + // Builds scope.inventory_groups, used by the group picker on Hosts view to build the list of potential groups + // that can be added to a host. <<<< Should probably be moved to /helpers/Hosts.js + if (scope.buildGroupListRemove) { + scope.buildGroupListRemove(); + } + scope.buildGroupListRemove = scope.$on('buildAllGroups', function(e, inventory_name, inventory_tree, groups_url) { + scope.inventory_groups = []; + Rest.setUrl(groups_url); + Rest.get() + .success( function(data, status, headers, config) { + var groups = []; + for (var i=0; i < data.results.length; i++) { + groups.push({ + id: data.results[i].id, + description: data.results[i].description, + name: data.results[i].name }); + } + scope.inventory_groups = SortNodes(groups); + }) + .error( function(data, status, headers, config) { + ProcessErrors(scope, data, status, null, + { hdr: 'Error!', msg: 'Failed to get groups for inventory: ' + inventory_id + '. GET returned: ' + status }); + }); + }); + + Wait('start'); + + // Load the inventory root node + Rest.setUrl (GetBasePath('inventory') + inventory_id + '/'); + Rest.get() + .success( function(data, status, headers, config) { + html += "
Group Selector:
\n" + + "