Added drag/drop for Role Assign page

Updated dragdrop lib

Change-Id: I2a66b8bdf2b8cc03b648ce2fc647a6220be9464d
This commit is contained in:
Shuai Zhu 2014-09-16 17:11:42 -07:00
parent 6dc22eaf8a
commit e44bc3f6e2
5 changed files with 392 additions and 124 deletions

View File

@ -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;
}

View File

@ -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, {}];
})
});
});

View File

@ -76,61 +76,79 @@
</div>
</div>
<div class="row">
<div class="space-6"></div>
<table ng-table="tableParams" class="table table-hover table-striped">
<thead>
<tr>
<th>
<label>
<input ng-model="selectall" ng-change="selectAllServers(selectall)" type="checkbox" class="ace">
<span class="lbl"></span>
</label>
</th>
<th ng-repeat="column in server_columns" ng-show="column.visible" class="sortable" ng-class="{'sort-asc': tableParams.isSortBy(column.field, 'asc'),
'sort-desc': tableParams.isSortBy(column.field, 'desc')}" ng-click="tableParams.sorting(column.field, tableParams.isSortBy(column.field, 'asc') ? 'desc' : 'asc')">
<div>{{column.title}}</div>
</th>
<th>Roles</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="server in $data | filter: search" ng-init="server.roles = server.roles === undefinded? [] : server.roles" ng-class="{'hightlight': server.checked}">
<td>
<label>
<input ng-model="server.checked" type="checkbox" class="ace">
<span class="lbl"></span>
</label>
</td>
<td ng-repeat="column in server_columns" ng-show="column.visible" sortable="column.field">
<span ng-switch on="column.field">
<span ng-switch-when="os_installed">
<span ng-if="server['os']">
<label>
<input ng-model="server.reinstallos" type="checkbox" class="ace">
<span class="lbl"></span>
</label>
<div class="role-col role-table-padding">
<div class="space-6"></div>
<table ng-table="tableParams" class="table table-hover table-striped">
<thead>
<tr>
<th>
<label>
<input ng-model="selectall" ng-change="selectAllServers(selectall)" type="checkbox" class="ace">
<span class="lbl"></span>
</label>
</th>
<th ng-repeat="column in server_columns" ng-show="column.visible" class="sortable" ng-class="{'sort-asc': tableParams.isSortBy(column.field, 'asc'),
'sort-desc': tableParams.isSortBy(column.field, 'desc')}" ng-click="tableParams.sorting(column.field, tableParams.isSortBy(column.field, 'asc') ? 'desc' : 'asc')">
<div>{{column.title}}</div>
</th>
<th>Roles</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="(key, value) in $data | filter: search" ng-init="value.roles = value.roles === undefinded? [] : value.roles" ng-class="{'hightlight': value.checked}">
<td>
<label>
<input ng-model="value.checked" type="checkbox" class="ace">
<span class="lbl"></span>
</label>
</td>
<td ng-repeat="column in server_columns" ng-show="column.visible" sortable="column.field">
<span ng-switch on="column.field">
<span ng-switch-when="os_installed">
<span ng-if="value['os']">
<label>
<input ng-model="value.reinstallos" type="checkbox" class="ace">
<span class="lbl"></span>
</label>
</span>
<span ng-if="!value['os']">
-
</span>
</span>
<span ng-if="!server['os']">
-
<span ng-switch-when="clusters">
<span ng-repeat="cluster in value.clusters">
{{cluster.name}}&nbsp;
</span>
</span>
<span ng-switch-default>
{{value[column.field]}}
</span>
</span>
<span ng-switch-when="clusters">
<span ng-repeat="cluster in server.clusters">
{{cluster.name}}&nbsp;
</span>
</span>
<span ng-switch-default>
{{server[column.field]}}
</span>
</span>
</td>
<td>
<alert ng-repeat="role in server['roles']" class="role-tag" ng-style="{'background-color': role.color}" close="removeRole(server, role)">
{{role.display_name}}
</alert>
</td>
</tr>
</tbody>
</table>
</td>
<td>
<div class="role-assign-drop" ui-on-Drop="onDrop($event, key)" drag-hover-class="drag-enter-role" drop-channel="{{value.dropChannel}}">
<alert ng-repeat="role in value['roles']" class="role-tag border-radius-4" ng-style="{'background-color': role.color}" close="removeRole(value, role)">
{{role.display_name}}
</alert>
</div>
</td>
</tr>
</tbody>
</table>
</div>
<div class="role-col drag-role-col">
<div class="widget-box transparent role-widget-box">
<div class="widget-header widget-header-marginbottom">
<br>
<h4 class="margin-left-neg4">Drag Roles <br> to Assign</h4>
</div>
<div class="role-assign-drag">
<div ui-draggable="true" on-drop-success="dropSuccessHandler($event,role_value,role_key)" class="role-tag drag-roles ui-draggable" ng-repeat="(role_key, role_value) in roles" ng-style="{'background-color': role_value.color}" drag-channel="{{role_value.dragChannel}}">
<i class="ace-icon fa fa-arrows margin-left-neg4"></i>
<span>{{role_value.display_name}}</span>
</div>
</div>
</div>
</div>
</div>
</div>

View File

@ -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) {

View File

@ -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
};
}
}
]);
}());