-
Notifications
You must be signed in to change notification settings - Fork 8.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
drag and drop panels, lots of visual tweaks
- Loading branch information
Rashid Khan
committed
Sep 26, 2013
1 parent
f2e6d44
commit 6427f36
Showing
3 changed files
with
5,616 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
define([ | ||
'angular', | ||
'underscore' | ||
], | ||
function (angular, _) { | ||
'use strict'; | ||
|
||
var module = angular.module('kibana.services'); | ||
|
||
module.service('panelMove', function(dashboard, $rootScope, alertSrv) { | ||
|
||
/* each of these can take event,ui,data parameters */ | ||
|
||
var notices = []; | ||
|
||
this.onStart = function() { | ||
dashboard.panelDragging = true; | ||
notices.push(alertSrv.set('Moving','Drop this panel into an empty space, or on top of another panel','info')); | ||
$rootScope.$apply(); | ||
}; | ||
|
||
this.onOver = function(event,ui,data,replace) { | ||
if(replace) { | ||
notices.push(alertSrv.set('Swap panel', | ||
'Drop to swap these panels. Panels will use row height, but retain their span','success')); | ||
} else { | ||
notices.push(alertSrv.set('Add panel', | ||
'Drop to add to this row. Panel will use row height, but retain their span','success')); | ||
} | ||
$rootScope.$apply(); | ||
}; | ||
|
||
this.onOut = function() { | ||
clearNotices({severity:'success'}); | ||
$rootScope.$apply(); | ||
}; | ||
|
||
this.onDrop = function() { | ||
dashboard.panelDragging = false; | ||
// Cleanup nulls/undefined left behind | ||
cleanup(); | ||
$rootScope.$apply(); | ||
$rootScope.$broadcast('render'); | ||
}; | ||
|
||
this.onStop = function() { | ||
dashboard.panelDragging = false; | ||
cleanup(); | ||
$rootScope.$apply(); | ||
}; | ||
|
||
var cleanup = function () { | ||
_.each(notices, function(n){ | ||
alertSrv.clear(n); | ||
}); | ||
_.each(dashboard.current.rows, function(row) { | ||
row.panels = _.without(row.panels,{}); | ||
row.panels = _.compact(row.panels); | ||
}); | ||
}; | ||
|
||
var clearNotices = function(options) { | ||
_.each(_.where(notices,options), function(n) { | ||
alertSrv.clear(n); | ||
}); | ||
}; | ||
|
||
}); | ||
|
||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,293 @@ | ||
/* | ||
This has been modified from the default angular-draganddrop to provide the original | ||
model and some other stuff as the 'data' arguement to callEventCallback | ||
*/ | ||
|
||
(function (window, angular, undefined) { | ||
'use strict'; | ||
|
||
var jqyoui = angular.module('ngDragDrop', []).service('ngDragDropService', ['$timeout', '$parse', function($timeout, $parse) { | ||
this.callEventCallback = function (scope, callbackName, event, ui, data) { | ||
if (!callbackName) { | ||
return; | ||
} | ||
var args = [event, ui, data]; | ||
var match = callbackName.match(/^(.+)\((.+)\)$/); | ||
if (match !== null) { | ||
callbackName = match[1]; | ||
var values = eval('[' + match[0].replace(/^(.+)\(/, '').replace(/\)/, '') + ']'); | ||
args.push.apply(args, values); | ||
} | ||
if(scope[callbackName]) { | ||
scope[callbackName].apply(scope, args); | ||
} | ||
}; | ||
|
||
this.invokeDrop = function ($draggable, $droppable, event, ui) { | ||
var dragModel = '', | ||
dropModel = '', | ||
dragSettings = {}, | ||
dropSettings = {}, | ||
jqyoui_pos = null, | ||
dragItem = {}, | ||
dropItem = {}, | ||
dragModelValue, | ||
dropModelValue, | ||
$droppableDraggable = null, | ||
droppableScope = $droppable.scope(), | ||
draggableScope = $draggable.scope(), | ||
data = {}; | ||
|
||
dragModel = $draggable.ngattr('ng-model'); | ||
dropModel = $droppable.ngattr('ng-model'); | ||
dragModelValue = draggableScope.$eval(dragModel); | ||
dropModelValue = droppableScope.$eval(dropModel); | ||
|
||
$droppableDraggable = $droppable.find('[jqyoui-draggable]:last'); | ||
dropSettings = droppableScope.$eval($droppable.attr('jqyoui-droppable')) || []; | ||
dragSettings = draggableScope.$eval($draggable.attr('jqyoui-draggable')) || []; | ||
|
||
// Helps pick up the right item | ||
dragSettings.index = this.fixIndex(draggableScope, dragSettings, dragModelValue); | ||
dropSettings.index = this.fixIndex(droppableScope, dropSettings, dropModelValue); | ||
|
||
jqyoui_pos = angular.isArray(dragModelValue) ? dragSettings.index : null; | ||
dragItem = angular.isArray(dragModelValue) ? dragModelValue[jqyoui_pos] : dragModelValue; | ||
|
||
if (angular.isArray(dropModelValue) && dropSettings && dropSettings.index !== undefined) { | ||
dropItem = dropModelValue[dropSettings.index]; | ||
} else if (!angular.isArray(dropModelValue)) { | ||
dropItem = dropModelValue; | ||
} else { | ||
dropItem = {}; | ||
} | ||
|
||
data = { | ||
dragModel: dragModel, | ||
dropModel: dropModel, | ||
dragSettings: dragSettings, | ||
dropSettings: dropSettings, | ||
jqyoui_pos: jqyoui_pos, | ||
dragItem: dragItem, | ||
dropItem: dropItem, | ||
dragModelValue: dragModelValue, | ||
dropModelValue: dropModelValue, | ||
droppableScope: $droppable.scope(), | ||
draggableScope: $draggable.scope() | ||
}; | ||
|
||
|
||
if (dragSettings.animate === true) { | ||
|
||
this.move($draggable, $droppableDraggable.length > 0 ? $droppableDraggable : $droppable, null, 'fast', dropSettings, null); | ||
this.move($droppableDraggable.length > 0 && !dropSettings.multiple ? $droppableDraggable : [], $draggable.parent('[jqyoui-droppable]'), jqyoui.startXY, 'fast', dropSettings, function() { | ||
$timeout(function() { | ||
// Do not move this into move() to avoid flickering issue | ||
$draggable.css({'position': 'relative', 'left': '', 'top': ''}); | ||
$droppableDraggable.css({'position': 'relative', 'left': '', 'top': ''}); | ||
|
||
this.mutateDraggable(draggableScope, dropSettings, dragSettings, dragModel, dropModel, dropItem, $draggable); | ||
this.mutateDroppable(droppableScope, dropSettings, dragSettings, dropModel, dragItem, jqyoui_pos); | ||
|
||
this.callEventCallback(droppableScope, dropSettings.onDrop, event, ui, data); | ||
}.bind(this)); | ||
}.bind(this)); | ||
} else { | ||
$timeout(function() { | ||
this.mutateDraggable(draggableScope, dropSettings, dragSettings, dragModel, dropModel, dropItem, $draggable); | ||
this.mutateDroppable(droppableScope, dropSettings, dragSettings, dropModel, dragItem, jqyoui_pos); | ||
|
||
this.callEventCallback(droppableScope, dropSettings.onDrop, event, ui, data); | ||
}.bind(this)); | ||
} | ||
}; | ||
|
||
this.move = function($fromEl, $toEl, toPos, duration, dropSettings, callback) { | ||
if ($fromEl.length === 0) { | ||
if (callback) { | ||
window.setTimeout(function() { | ||
callback(); | ||
}, 300); | ||
} | ||
return false; | ||
} | ||
|
||
var zIndex = 9999, | ||
fromPos = $fromEl.offset(), | ||
wasVisible = $toEl && $toEl.is(':visible'); | ||
|
||
if (toPos === null && $toEl.length > 0) { | ||
if ($toEl.attr('jqyoui-draggable') !== undefined && $toEl.ngattr('ng-model') !== undefined && $toEl.is(':visible') && dropSettings && dropSettings.multiple) { | ||
toPos = $toEl.offset(); | ||
if (dropSettings.stack === false) { | ||
toPos.left+= $toEl.outerWidth(true); | ||
} else { | ||
toPos.top+= $toEl.outerHeight(true); | ||
} | ||
} else { | ||
toPos = $toEl.css({'visibility': 'hidden', 'display': 'block'}).offset(); | ||
$toEl.css({'visibility': '','display': wasVisible ? '' : 'none'}); | ||
} | ||
} | ||
|
||
$fromEl.css({'position': 'absolute', 'z-index': zIndex}) | ||
.css(fromPos) | ||
.animate(toPos, duration, function() { | ||
if (callback) callback(); | ||
}); | ||
}; | ||
|
||
this.mutateDroppable = function(scope, dropSettings, dragSettings, dropModel, dragItem, jqyoui_pos) { | ||
var dropModelValue = scope.$eval(dropModel); | ||
|
||
scope.__dragItem = dragItem; | ||
|
||
if (angular.isArray(dropModelValue)) { | ||
if (dropSettings && dropSettings.index >= 0) { | ||
dropModelValue[dropSettings.index] = dragItem; | ||
} else { | ||
dropModelValue.push(dragItem); | ||
} | ||
if (dragSettings && dragSettings.placeholder === true) { | ||
dropModelValue[dropModelValue.length - 1]['jqyoui_pos'] = jqyoui_pos; | ||
} | ||
} else { | ||
$parse(dropModel + ' = __dragItem')(scope); | ||
if (dragSettings && dragSettings.placeholder === true) { | ||
dropModelValue['jqyoui_pos'] = jqyoui_pos; | ||
} | ||
} | ||
}; | ||
|
||
this.mutateDraggable = function(scope, dropSettings, dragSettings, dragModel, dropModel, dropItem, $draggable) { | ||
var isEmpty = angular.equals(angular.copy(dropItem), {}), | ||
dragModelValue = scope.$eval(dragModel); | ||
|
||
scope.__dropItem = dropItem; | ||
|
||
if (dragSettings && dragSettings.placeholder) { | ||
if (dragSettings.placeholder != 'keep'){ | ||
if (angular.isArray(dragModelValue) && dragSettings.index !== undefined) { | ||
dragModelValue[dragSettings.index] = dropItem; | ||
} else { | ||
$parse(dragModel + ' = __dropItem')(scope); | ||
} | ||
} | ||
} else { | ||
if (angular.isArray(dragModelValue)) { | ||
if (isEmpty) { | ||
if (dragSettings && ( dragSettings.placeholder !== true && dragSettings.placeholder !== 'keep' )) { | ||
dragModelValue.splice(dragSettings.index, 1); | ||
} | ||
} else { | ||
dragModelValue[dragSettings.index] = dropItem; | ||
} | ||
} else { | ||
// Fix: LIST(object) to LIST(array) - model does not get updated using just scope[dragModel] = {...} | ||
// P.S.: Could not figure out why it happened | ||
$parse(dragModel + ' = __dropItem')(scope); | ||
if (scope.$parent) { | ||
$parse(dragModel + ' = __dropItem')(scope.$parent); | ||
} | ||
} | ||
} | ||
|
||
$draggable.css({'z-index': '', 'left': '', 'top': ''}); | ||
}; | ||
|
||
this.fixIndex = function(scope, settings, modelValue) { | ||
if (settings.applyFilter && angular.isArray(modelValue) && modelValue.length > 0) { | ||
var dragModelValueFiltered = scope[settings.applyFilter](), | ||
lookup = dragModelValueFiltered[settings.index], | ||
actualIndex = undefined; | ||
|
||
modelValue.forEach(function(item, i) { | ||
if (angular.equals(item, lookup)) { | ||
actualIndex = i; | ||
} | ||
}); | ||
|
||
return actualIndex; | ||
} | ||
|
||
return settings.index; | ||
}; | ||
}]).directive('jqyouiDraggable', ['ngDragDropService', function(ngDragDropService) { | ||
return { | ||
require: '?jqyouiDroppable', | ||
restrict: 'A', | ||
link: function(scope, element, attrs) { | ||
var dragSettings, zIndex; | ||
var updateDraggable = function(newValue, oldValue) { | ||
if (newValue) { | ||
dragSettings = scope.$eval(element.attr('jqyoui-draggable')) || []; | ||
element | ||
.draggable({disabled: false}) | ||
.draggable(scope.$eval(attrs.jqyouiOptions) || {}) | ||
.draggable({ | ||
start: function(event, ui) { | ||
zIndex = angular.element(this).css('z-index'); | ||
angular.element(this).css('z-index', 99999); | ||
jqyoui.startXY = angular.element(this).offset(); | ||
ngDragDropService.callEventCallback(scope, dragSettings.onStart, event, ui); | ||
}, | ||
stop: function(event, ui) { | ||
angular.element(this).css('z-index', zIndex); | ||
ngDragDropService.callEventCallback(scope, dragSettings.onStop, event, ui); | ||
}, | ||
drag: function(event, ui) { | ||
ngDragDropService.callEventCallback(scope, dragSettings.onDrag, event, ui); | ||
} | ||
}); | ||
} else { | ||
element.draggable({disabled: true}); | ||
} | ||
}; | ||
scope.$watch(function() { return scope.$eval(attrs.drag); }, updateDraggable); | ||
updateDraggable(); | ||
} | ||
}; | ||
}]).directive('jqyouiDroppable', ['ngDragDropService', function(ngDragDropService) { | ||
return { | ||
restrict: 'A', | ||
priority: 1, | ||
link: function(scope, element, attrs) { | ||
var updateDroppable = function(newValue, oldValue) { | ||
if (newValue) { | ||
element | ||
.droppable({disabled: false}) | ||
.droppable(scope.$eval(attrs.jqyouiOptions) || {}) | ||
.droppable({ | ||
over: function(event, ui) { | ||
var dropSettings = scope.$eval(angular.element(this).attr('jqyoui-droppable')) || []; | ||
ngDragDropService.callEventCallback(scope, dropSettings.onOver, event, ui); | ||
}, | ||
out: function(event, ui) { | ||
var dropSettings = scope.$eval(angular.element(this).attr('jqyoui-droppable')) || []; | ||
ngDragDropService.callEventCallback(scope, dropSettings.onOut, event, ui); | ||
}, | ||
drop: function(event, ui) { | ||
if (angular.element(ui.draggable).ngattr('ng-model') && attrs.ngModel) { | ||
ngDragDropService.invokeDrop(angular.element(ui.draggable), angular.element(this), event, ui); | ||
} else { | ||
ngDragDropService.callEventCallback(scope, (scope.$eval(angular.element(this).attr('jqyoui-droppable')) || []).onDrop, event, ui); | ||
} | ||
} | ||
}); | ||
} else { | ||
element.droppable({disabled: true}); | ||
} | ||
}; | ||
|
||
scope.$watch(function() { return scope.$eval(attrs.drop); }, updateDroppable); | ||
updateDroppable(); | ||
} | ||
}; | ||
}]); | ||
|
||
$.fn.ngattr = function(name, value) { | ||
var element = angular.element(this).get(0); | ||
|
||
return element.getAttribute(name) || element.getAttribute('data-' + name); | ||
}; | ||
})(window, window.angular); |
Oops, something went wrong.