From e44bc3f6e2a653662daaa930867febb2fe4d9e4d Mon Sep 17 00:00:00 2001 From: Shuai Zhu Date: Tue, 16 Sep 2014 17:11:42 -0700 Subject: [PATCH] Added drag/drop for Role Assign page Updated dragdrop lib Change-Id: I2a66b8bdf2b8cc03b648ce2fc647a6220be9464d --- v2/assets/css/style.css | 55 ++++- v2/src/app/appDev.js | 43 +++- v2/src/app/wizard/role_assignment.tpl.html | 124 +++++++----- v2/src/app/wizard/wizard.js | 69 ++++++- v2/vendor/angular-dragdrop/draganddrop.js | 225 +++++++++++++++++---- 5 files changed, 392 insertions(+), 124 deletions(-) diff --git a/v2/assets/css/style.css b/v2/assets/css/style.css index d85c89d..f363c14 100644 --- a/v2/assets/css/style.css +++ b/v2/assets/css/style.css @@ -520,7 +520,7 @@ a:active { tr.hightlight { background-color: #e4efc9 !important; } -tr.hightlight td{ +tr.hightlight td { background-color: #e4efc9 !important; } .btn-previous { @@ -608,6 +608,7 @@ div.center-align { } .widget-header-marginbottom { border-bottom: 1px solid #bed2db !important; + margin-bottom: 15px; } .network-background { background-image: url(); @@ -948,7 +949,6 @@ circle[depth='4'] { display: block !important; } } - .angular-ui-tree-handle { background: #f8faff; border: 1px solid #dae2ea; @@ -956,13 +956,11 @@ circle[depth='4'] { padding: 10px 10px; cursor: default; } - .angular-ui-tree-handle:hover { color: #438eb9; background: #f4f6f7; border-color: #dce2e8; } - .angular-ui-tree-placeholder { background: #f0f9ff; border: 2px dashed #bed2db; @@ -970,9 +968,54 @@ circle[depth='4'] { -moz-box-sizing: border-box; box-sizing: border-box; } - - .group-title { background-color: #687074 !important; color: #FFF !important; } +.role-assign-drop { + min-height: 25px; + min-width: 200px; + /* background-color: rgba(221, 221, 221, 0.32); */ +} +.role-assign-drag { + border: 1px solid rgba(190, 210, 219, 0.5) !important; + border-radius: 4px; + min-height: 400px; + min-width: 120px; +} +.role-table-padding { + padding: 0px 0px 15px 0px !important; + width: 87%; +} +.role-widget-box { + position: fixed !important; + margin-left: 0px !important; +} +.margin-left-neg4 { + margin-left: -4px; +} +.drag-roles { + margin-left: 12px; + margin-top: 7px; + display: block !important; + width: 95px !important; + border-radius: 4px !important; +} +.drag-role-col { + width: 13%; +} +.role-col { + float: left !important; + min-height: 1px; + padding-left: 15px; + padding-right: 15px; + position: relative; +} +.drag-enter-role { + background: none repeat scroll 0 0 #F0F9FF; + border: 2px dashed #BED2DB !important; + box-sizing: border-box; + margin: 5px 0; + min-height: 35px; + padding: 0; +} \ No newline at end of file diff --git a/v2/src/app/appDev.js b/v2/src/app/appDev.js index c171001..149bae6 100644 --- a/v2/src/app/appDev.js +++ b/v2/src/app/appDev.js @@ -390,13 +390,14 @@ compassAppDev.run(function($httpBackend, settings, $http) { }]; return [200, machines, {}]; }); + /* $httpBackend.whenPOST(settings.apiUrlBase + '/switch-filters').respond(function(method, url, data) { console.log(method, url, data); var filterData = JSON.parse(data); return [200, filterData, {}]; }); -*/ + */ $httpBackend.whenPUT(/\.*\/switch-filters\/([0-9]|[1-9][0-9])/).respond(function(method, url, data) { console.log(method, url, data); var filterData = JSON.parse(data); @@ -650,6 +651,15 @@ compassAppDev.run(function($httpBackend, settings, $http) { }, { "display_name": "Network", "name": "os-network" + }, { + "display_name": "Storage", + "name": "os-block-storage-worker" + }, { + "display_name": "Message Queue", + "name": "os-mq" + }, { + "display_name": "database", + "name": "os-db" }] }, "links": [{ @@ -915,6 +925,26 @@ compassAppDev.run(function($httpBackend, settings, $http) { console.log(method, url, data); var hosts = []; var num = 20; + var test_roles = [{ + "display_name": "Compute", + "name": "os-compute-worker" + }, { + "display_name": "Controller", + "name": "os-controller" + }, { + "display_name": "Network", + "name": "os-network" + }, { + "display_name": "Storage", + "name": "os-block-storage-worker" + }, { + "display_name": "Message Queue", + "name": "os-mq" + }, { + "display_name": "database", + "name": "os-db" + }]; + for (var i = 1; i <= num; i++) { var host = { "id": i, @@ -925,14 +955,7 @@ compassAppDev.run(function($httpBackend, settings, $http) { "switch_ip": "172.29.8.40", "port": i, "vlan": i, - "roles": [{ - "display_name": "Network", - "name": "os-network" - }, { - "display_name": "Storage", - "name": "os-block-storage-worker" - }], - "os_name": "CentOS", + "roles": [test_roles[i%6]], "clusters": [{ "id": 1, "name": "cluster1" @@ -1002,4 +1025,4 @@ compassAppDev.run(function($httpBackend, settings, $http) { var deleteHost = {}; return [200, deleteHost, {}]; }) -}); \ No newline at end of file +}); diff --git a/v2/src/app/wizard/role_assignment.tpl.html b/v2/src/app/wizard/role_assignment.tpl.html index 9059fbf..b7ccbb6 100644 --- a/v2/src/app/wizard/role_assignment.tpl.html +++ b/v2/src/app/wizard/role_assignment.tpl.html @@ -76,61 +76,79 @@
-
- - - - - - - - - - - - + + + +
- - -
{{column.title}}
-
Roles
- - - - - - +
+
+ + + + + + + + + + + + - - - -
+ + +
{{column.title}}
+
Roles
+ + + + + + + + + - + - - - + + + {{cluster.name}}  + + + + {{value[column.field]}} - - - {{cluster.name}}  - - - - {{server[column.field]}} - - - - - {{role.display_name}} - -
+
+
+ + {{role.display_name}} + +
+
+
+
+
+
+
+

Drag Roles
to Assign

+
+
+
+ + {{role_value.display_name}} +
+
+
+
diff --git a/v2/src/app/wizard/wizard.js b/v2/src/app/wizard/wizard.js index 793a04d..e75e294 100644 --- a/v2/src/app/wizard/wizard.js +++ b/v2/src/app/wizard/wizard.js @@ -1117,29 +1117,43 @@ angular.module('compass.wizard', [ var cluster = wizardFactory.getClusterInfo(); $scope.servers = wizardFactory.getServers(); var colors = ['#8EA16C', '#C2CF30', '#FEC700', '#FF8900', '#D3432B', '#BB2952', '#8E1E5F', '#DE4AB6', '#9900EC', '#3A1AA8', '#3932FE', '#278BC0', '#35B9F6', '#91E0CB', '#42BC6A', '#5B4141']; + + $scope.existingRoles = []; + $scope.realRole = []; + + dataService.getServerColumns().success(function(data) { + $scope.server_columns = data.showless; + }); + dataService.getClusterById(cluster.id).success(function(data) { // wizardFactory.setAdapter(data); $scope.roles = data.flavor.roles; var i = 0; - angular.forEach($scope.roles, function(role) { + angular.forEach($scope.roles, function(role, role_key) { role.color = colors[i]; + $scope.roles[role_key].dragChannel = i; i++; - }) - }); - - dataService.getServerColumns().success(function(data) { - $scope.server_columns = data.showless; + }); + angular.forEach($scope.roles, function(role, role_key) { + //realRole.splice(z,0,z); + $scope.realRole.push(role_key); + }); + angular.forEach($scope.servers, function(value, key) { + $scope.existingRoles.push(angular.copy($scope.realRole)); + $scope.servers[key].dropChannel = $scope.existingRoles[key].toString(); + }); + $scope.checkExistRolesDrag(); }); $scope.selectAllServers = function(flag) { if (flag) { angular.forEach($scope.servers, function(sv) { sv.checked = true; - }) + }); } else { angular.forEach($scope.servers, function(sv) { sv.checked = false; - }) + }); } }; @@ -1147,6 +1161,14 @@ angular.module('compass.wizard', [ var serverIndex = $scope.servers.indexOf(server); var roleIndex = $scope.servers[serverIndex].roles.indexOf(role); $scope.servers[serverIndex].roles.splice(roleIndex, 1); + + angular.forEach($scope.roles, function(role_value, role_key) { + if (role.display_name == $scope.roles[role_key].display_name) { + $scope.existingRoles[serverIndex].splice(role_key, 1, role_key) + } + }); + + $scope.servers[serverIndex].dropChannel = $scope.existingRoles[serverIndex].toString(); }; $scope.assignRole = function(role) { @@ -1192,7 +1214,7 @@ angular.module('compass.wizard', [ } svIndex++; } - }) + }); }; $scope.checkRoleExist = function(existingRoles, newRole) { @@ -1310,6 +1332,33 @@ angular.module('compass.wizard', [ wizardFactory.setCommitState(commitState); }); }; + + $scope.onDrop = function($event, key) { + $scope.dragKey = key; + }; + + $scope.dropSuccessHandler = function($event, role_value, key) { + var roleExist = $scope.checkRoleExist($scope.servers[$scope.dragKey].roles, role_value); + if (!roleExist) { + $scope.servers[$scope.dragKey].roles.push(role_value); + } else { + console.log("role exists"); + } + $scope.checkExistRolesDrag(); + }; + + $scope.checkExistRolesDrag = function() { + angular.forEach($scope.servers, function(value, key) { + angular.forEach($scope.servers[key].roles, function(server_role, server_role_key) { + angular.forEach($scope.roles, function(role, role_key) { + if ($scope.servers[key].roles[server_role_key].display_name == $scope.roles[role_key].display_name) { + $scope.existingRoles[key].splice(role_key, 1, "p") + } + }) + }) + $scope.servers[key].dropChannel = $scope.existingRoles[key].toString(); + }) + }; }) .controller('networkMappingCtrl', function($scope, wizardFactory, dataService) { @@ -1326,7 +1375,6 @@ angular.module('compass.wizard', [ $scope.interfaces[key].dropChannel = "E"; }) - $scope.networking = {}; angular.forEach($scope.original_networking, function(value, key) { $scope.networking[key] = {}; @@ -1339,7 +1387,6 @@ angular.module('compass.wizard', [ $scope.dropSuccessHandler = function($event, key, dict) { dict[key].mapping_interface = $scope.pendingInterface; - console.log($scope.pendingInterface); }; angular.forEach($scope.interfaces, function(value, key) { diff --git a/v2/vendor/angular-dragdrop/draganddrop.js b/v2/vendor/angular-dragdrop/draganddrop.js index 7de17da..997c690 100644 --- a/v2/vendor/angular-dragdrop/draganddrop.js +++ b/v2/vendor/angular-dragdrop/draganddrop.js @@ -5,26 +5,45 @@ * Time: 11:27 * To change this template use File | Settings | File Templates. */ + +(function(){ + +function isDnDsSupported(){ + return 'draggable' in document.createElement("span"); +} + +if(!isDnDsSupported()){ + return; +} + if (window.jQuery && (-1 == window.jQuery.event.props.indexOf("dataTransfer"))) { window.jQuery.event.props.push("dataTransfer"); } +var currentData; + angular.module("ngDragDrop",[]) .directive("uiDraggable", [ '$parse', '$rootScope', - function ($parse, $rootScope) { + '$dragImage', + function ($parse, $rootScope, $dragImage) { return function (scope, element, attrs) { var dragData = "", isDragHandleUsed = false, dragHandleClass, - dragHandles, dragTarget; element.attr("draggable", false); attrs.$observe("uiDraggable", function (newValue) { - element.attr("draggable", newValue); + if(newValue){ + element.attr("draggable", newValue); + } + else{ + element.removeAttr("draggable"); + } + }); if (attrs.drag) { @@ -36,42 +55,16 @@ angular.module("ngDragDrop",[]) if (angular.isString(attrs.dragHandleClass)) { isDragHandleUsed = true; dragHandleClass = attrs.dragHandleClass.trim() || "drag-handle"; - dragHandles = element.find('.' + dragHandleClass).toArray(); element.bind("mousedown", function (e) { dragTarget = e.target; }); } - element.bind("dragstart", function (e) { - var isDragAllowed = !isDragHandleUsed || -1 != dragHandles.indexOf(dragTarget); - - if (isDragAllowed) { - var sendData = angular.toJson(dragData); - var sendChannel = attrs.dragChannel || "defaultchannel"; - var dragImage = attrs.dragImage || null; - if (dragImage) { - var dragImageFn = $parse(attrs.dragImage); - scope.$apply(function() { - var dragImageParameters = dragImageFn(scope, {$event: e}); - if (dragImageParameters && dragImageParameters.image) { - var xOffset = dragImageParameters.xOffset || 0, - yOffset = dragImageParameters.yOffset || 0; - e.dataTransfer.setDragImage(dragImageParameters.image, xOffset, yOffset); - } - }); - } - - e.dataTransfer.setData("text/plain", sendData); - e.dataTransfer.effectAllowed = "copyMove"; - $rootScope.$broadcast("ANGULAR_DRAG_START", sendChannel); - } - else { - e.preventDefault(); - } - }); - - element.bind("dragend", function (e) { + function dragendHandler(e) { + setTimeout(function() { + element.unbind('$destroy', dragendHandler); + }, 0); var sendChannel = attrs.dragChannel || "defaultchannel"; $rootScope.$broadcast("ANGULAR_DRAG_END", sendChannel); if (e.dataTransfer && e.dataTransfer.dropEffect !== "none") { @@ -80,11 +73,55 @@ angular.module("ngDragDrop",[]) scope.$apply(function () { fn(scope, {$event: e}); }); + } else { + if (attrs.onDropFailure) { + var fn = $parse(attrs.onDropFailure); + scope.$apply(function () { + fn(scope, {$event: e}); + }); + } } } + } + + element.bind("dragend", dragendHandler); + + element.bind("dragstart", function (e) { + var isDragAllowed = !isDragHandleUsed || dragTarget.classList.contains(dragHandleClass); + + if (isDragAllowed) { + var sendChannel = attrs.dragChannel || "defaultchannel"; + var sendData = angular.toJson({ data: dragData, channel: sendChannel }); + var dragImage = attrs.dragImage || null; + + element.bind('$destroy', dragendHandler); + + if (dragImage) { + var dragImageFn = $parse(attrs.dragImage); + scope.$apply(function() { + var dragImageParameters = dragImageFn(scope, {$event: e}); + if (dragImageParameters) { + if (angular.isString(dragImageParameters)) { + dragImageParameters = $dragImage.generate(dragImageParameters); + } + if (dragImageParameters.image) { + var xOffset = dragImageParameters.xOffset || 0, + yOffset = dragImageParameters.yOffset || 0; + e.dataTransfer.setDragImage(dragImageParameters.image, xOffset, yOffset); + } + } + }); + } + + e.dataTransfer.setData("Text", sendData); + currentData = angular.fromJson(sendData); + e.dataTransfer.effectAllowed = "copyMove"; + $rootScope.$broadcast("ANGULAR_DRAG_START", sendChannel); + } + else { + e.preventDefault(); + } }); - - }; } ]) @@ -94,7 +131,7 @@ angular.module("ngDragDrop",[]) function ($parse, $rootScope) { return function (scope, element, attr) { var dragging = 0; //Ref. http://stackoverflow.com/a/10906204 - var dropChannel = "defaultchannel"; + var dropChannel = attr.dropChannel || "defaultchannel" ; var dragChannel = ""; var dragEnterClass = attr.dragEnterClass || "on-drag-enter"; var dragHoverClass = attr.dragHoverClass || "on-drag-hover"; @@ -121,7 +158,7 @@ angular.module("ngDragDrop",[]) function onDragEnter(e) { dragging++; - $rootScope.$broadcast("ANGULAR_HOVER", dropChannel); + $rootScope.$broadcast("ANGULAR_HOVER", dragChannel); element.addClass(dragHoverClass); } @@ -132,13 +169,16 @@ angular.module("ngDragDrop",[]) if (e.stopPropagation) { e.stopPropagation(); // Necessary. Allows us to drop. } - var data = e.dataTransfer.getData("text/plain"); - data = angular.fromJson(data); + + var sendData = e.dataTransfer.getData("Text"); + sendData = angular.fromJson(sendData); + var fn = $parse(attr.uiOnDrop); scope.$apply(function () { - fn(scope, {$data: data, $event: e}); + fn(scope, {$data: sendData.data, $event: e, $channel: sendData.channel}); }); element.removeClass(dragEnterClass); + dragging = 0; } function isDragChannelAccepted(dragChannel, dropChannel) { @@ -151,9 +191,31 @@ angular.module("ngDragDrop",[]) return channelMatchPattern.test("," + dropChannel + ","); } - $rootScope.$on("ANGULAR_DRAG_START", function (event, channel) { + function preventNativeDnD(e) { + if (e.preventDefault) { + e.preventDefault(); + } + if (e.stopPropagation) { + e.stopPropagation(); + } + e.dataTransfer.dropEffect = "none"; + return false; + } + + var deregisterDragStart = $rootScope.$on("ANGULAR_DRAG_START", function (event, channel) { dragChannel = channel; - if (isDragChannelAccepted(dragChannel, dropChannel)) { + if (isDragChannelAccepted(channel, dropChannel)) { + if (attr.dropValidate) { + var validateFn = $parse(attr.dropValidate); + var valid = validateFn(scope, {$data: currentData.data, $channel: currentData.channel}); + if (!valid) { + element.bind("dragover", preventNativeDnD); + element.bind("dragenter", preventNativeDnD); + element.bind("dragleave", preventNativeDnD); + element.bind("drop", preventNativeDnD); + return; + } + } element.bind("dragover", onDragOver); element.bind("dragenter", onDragEnter); @@ -162,12 +224,18 @@ angular.module("ngDragDrop",[]) element.bind("drop", onDrop); element.addClass(dragEnterClass); } + else { + element.bind("dragover", preventNativeDnD); + element.bind("dragenter", preventNativeDnD); + element.bind("dragleave", preventNativeDnD); + element.bind("drop", preventNativeDnD); + } }); - $rootScope.$on("ANGULAR_DRAG_END", function (e, channel) { + var deregisterDragEnd = $rootScope.$on("ANGULAR_DRAG_END", function (e, channel) { dragChannel = ""; if (isDragChannelAccepted(channel, dropChannel)) { @@ -179,16 +247,28 @@ angular.module("ngDragDrop",[]) element.removeClass(dragHoverClass); element.removeClass(dragEnterClass); } + + element.unbind("dragover", preventNativeDnD); + element.unbind("dragenter", preventNativeDnD); + element.unbind("dragleave", preventNativeDnD); + element.unbind("drop", preventNativeDnD); }); - $rootScope.$on("ANGULAR_HOVER", function (e, channel) { + var deregisterDragHover = $rootScope.$on("ANGULAR_HOVER", function (e, channel) { if (isDragChannelAccepted(channel, dropChannel)) { element.removeClass(dragHoverClass); } }); + scope.$on('$destroy', function () { + deregisterDragStart(); + deregisterDragEnd(); + deregisterDragHover(); + }); + + attr.$observe('dropChannel', function (value) { if (value) { dropChannel = value; @@ -198,4 +278,61 @@ angular.module("ngDragDrop",[]) }; } + ]) + .constant("$dragImageConfig", { + height: 20, + width: 200, + padding: 10, + font: 'bold 11px Arial', + fontColor: '#eee8d5', + backgroundColor: '#93a1a1', + xOffset: 0, + yOffset: 0 + }) + .service("$dragImage", [ + '$dragImageConfig', + function (defaultConfig) { + var ELLIPSIS = '…'; + + function fitString(canvas, text, config) { + var width = canvas.measureText(text).width; + if (width < config.width) { + return text; + } + while (width + config.padding > config.width) { + text = text.substring(0, text.length - 1); + width = canvas.measureText(text + ELLIPSIS).width; + } + return text + ELLIPSIS; + }; + + this.generate = function (text, options) { + var config = angular.extend({}, defaultConfig, options || {}); + var el = document.createElement('canvas'); + + el.height = config.height; + el.width = config.width; + + var canvas = el.getContext('2d'); + + canvas.fillStyle = config.backgroundColor; + canvas.fillRect(0, 0, config.width, config.height); + canvas.font = config.font; + canvas.fillStyle = config.fontColor; + + var title = fitString(canvas, text, config); + canvas.fillText(title, 4, config.padding + 4); + + var image = new Image(); + image.src = el.toDataURL(); + + return { + image: image, + xOffset: config.xOffset, + yOffset: config.yOffset + }; + } + } ]); + +}()); \ No newline at end of file