angular-stackables
Advanced tools
Comparing version 0.0.5 to 0.0.6
{ | ||
"name": "angular-stackables", | ||
"version": "0.0.5", | ||
"version": "0.0.6", | ||
"description": "AngularJS stackable widgets built on HTML5 dialog.", | ||
@@ -5,0 +5,0 @@ "authors": [ |
{ | ||
"name": "angular-stackables", | ||
"version": "0.0.5", | ||
"version": "0.0.6", | ||
"dependencies": {} | ||
} |
@@ -24,2 +24,3 @@ /*! | ||
modal: '=?stackableModal', | ||
persist: '=?persistContent', | ||
disableEscape: '=?stackableDisableEscape', | ||
@@ -35,3 +36,3 @@ closing: '&?stackableClosing', | ||
ng-show="show"> \ | ||
<div ng-if="show" class="stackable-content"> \ | ||
<div ng-if="show || persist" class="stackable-content"> \ | ||
<div ng-transclude></div> \ | ||
@@ -127,108 +128,2 @@ </div> \ | ||
function stackableDirective() { | ||
return { | ||
scope: { | ||
show: '=stackable', | ||
modal: '=?stackableModal', | ||
disableEscape: '=?stackableDisableEscape', | ||
closing: '&?stackableClosing', | ||
closed: '&?stackableClosed' | ||
}, | ||
restrict: 'A', | ||
replace: true, | ||
transclude: true, | ||
template: ' \ | ||
<dialog class="stackable" ng-class="{\'stackable-modal\': modal}" \ | ||
ng-show="show"> \ | ||
<div ng-if="show" class="stackable-content"> \ | ||
<div ng-transclude></div> \ | ||
</div> \ | ||
</dialog>', | ||
controller: ['$scope', Controller], | ||
link: Link | ||
}; | ||
function Controller($scope) { | ||
var self = this; | ||
var stackable = $scope.stackable = self; | ||
// close the stackable unless 'closing' callback aborts | ||
self.close = function(err, result) { | ||
var closing = $scope.closing || angular.noop; | ||
var shouldClose = closing.call($scope.$parent, { | ||
err: err, | ||
result: result | ||
}); | ||
Promise.resolve(shouldClose).then(function() { | ||
if(shouldClose !== false) { | ||
stackable.error = err; | ||
stackable.result = result; | ||
$scope.show = false; | ||
$scope.$apply(); | ||
} | ||
}); | ||
}; | ||
} | ||
function Link(scope, element) { | ||
var open = false; | ||
var body = angular.element('body'); | ||
var dialog = element[0]; | ||
// use polyfill if necessary | ||
if(!dialog.showModal && typeof dialogPolyfill !== 'undefined') { | ||
dialogPolyfill.registerDialog(dialog); | ||
} | ||
dialog.addEventListener('cancel', function(e) { | ||
if(!!scope.disableEscape) { | ||
e.preventDefault(); | ||
} else { | ||
scope.stackable.error = 'canceled'; | ||
scope.stackable.result = null; | ||
} | ||
}); | ||
dialog.addEventListener('close', function(e) { | ||
e.stopPropagation(); | ||
scope.show = open = false; | ||
var count = body.data('stackables') - 1; | ||
body.data('stackables', count); | ||
if(count === 0) { | ||
body.removeClass('stackable-modal-open'); | ||
} | ||
scope.$apply(); | ||
if(scope.closed) { | ||
scope.closed.call(scope.$parent, { | ||
err: scope.stackable.error, | ||
result: scope.stackable.result | ||
}); | ||
} | ||
}); | ||
scope.$watch('show', function(value) { | ||
if(value) { | ||
if(!open) { | ||
if(!!scope.modal) { | ||
dialog.showModal(); | ||
body.addClass('stackable-modal-open'); | ||
} else { | ||
dialog.show(); | ||
} | ||
open = true; | ||
scope.stackable.error = scope.stackable.result = undefined; | ||
var count = body.data('stackables') || 0; | ||
body.data('stackables', count + 1); | ||
} | ||
} else if(open) { | ||
// schedule dialog close to avoid $digest already in progress | ||
// as 'close' event handler may be called from here or externally | ||
setTimeout(function() { | ||
dialog.close(); | ||
}); | ||
open = false; | ||
} | ||
}); | ||
} | ||
} | ||
function stackableCancelDirective() { | ||
@@ -260,15 +155,17 @@ return { | ||
template: ' \ | ||
<div class="stackable-popover" ng-class="{ \ | ||
\'stackable-place-top\': !placement || placement == \'top\', \ | ||
\'stackable-place-right\': placement == \'right\', \ | ||
\'stackable-place-bottom\': placement == \'bottom\', \ | ||
\'stackable-place-left\': placement == \'left\', \ | ||
\'stackable-align-center\': !alignment || alignment == \'center\', \ | ||
\'stackable-align-top\': alignment == \'top\', \ | ||
\'stackable-align-right\': alignment == \'right\', \ | ||
\'stackable-align-bottom\': alignment == \'bottom\', \ | ||
\'stackable-align-left\': alignment == \'left\', \ | ||
\'stackable-no-arrow\': hideArrow}"> \ | ||
<div stackable="state.show"> \ | ||
<div class="stackable-popover-content"> \ | ||
<div> \ | ||
<div stackable="state.show" class="stackable-popover"> \ | ||
<div class="stackable-popover-content" style="display:none" \ | ||
ng-class="{ \ | ||
\'stackable-place-top\': !placement || placement == \'top\', \ | ||
\'stackable-place-right\': placement == \'right\', \ | ||
\'stackable-place-bottom\': placement == \'bottom\', \ | ||
\'stackable-place-left\': placement == \'left\', \ | ||
\'stackable-align-center\': !alignment || \ | ||
alignment == \'center\', \ | ||
\'stackable-align-top\': alignment == \'top\', \ | ||
\'stackable-align-right\': alignment == \'right\', \ | ||
\'stackable-align-bottom\': alignment == \'bottom\', \ | ||
\'stackable-align-left\': alignment == \'left\', \ | ||
\'stackable-no-arrow\': hideArrow}"> \ | ||
<div ng-if="!hideArrow" class="stackable-arrow"></div> \ | ||
@@ -279,120 +176,105 @@ <div ng-transclude></div> \ | ||
</div>', | ||
compile: Compile | ||
link: Link | ||
}; | ||
function Compile(tElement, tAttrs, transcludeFn) { | ||
var extents = {}; | ||
return function(scope, element) { | ||
// measure popover content | ||
transcludeFn(scope.$parent, function(clone) { | ||
var content = angular.element(' \ | ||
<div class="stackable-popover-content" style="width:auto"> \ | ||
<div ng-if="!hideArrow" class="stackable-arrow"></div> \ | ||
</div>'); | ||
content.append(clone); | ||
content.css({display: 'none'}); | ||
angular.element('body').append(content); | ||
extents.height = content.outerHeight(true); | ||
extents.width = content.outerWidth(true); | ||
// setTimeout hack to ensure content size has settled on chrome | ||
setTimeout(function() { | ||
extents.height = content.outerHeight(true); | ||
extents.width = content.outerWidth(true); | ||
content.remove(); | ||
}); | ||
function Link(scope, element) { | ||
var doc = angular.element(document); | ||
scope.$watch('state', function(state) { | ||
if(state) { | ||
if(state.show) { | ||
// close when pressing escape anywhere or clicking away | ||
doc.keyup(closeOnEscape).click(closeOnClick); | ||
} else { | ||
doc.off('keyup', closeOnEscape).off('click', closeOnClick); | ||
} | ||
} | ||
// schedule repositioning | ||
setTimeout(function() { | ||
// only reposition if content is shown | ||
var content = element.find('.stackable-popover-content'); | ||
if(!content.length) { | ||
return; | ||
} | ||
reposition(content); | ||
}); | ||
}, true); | ||
// whenever state changes, reposition popover | ||
scope.$watch('state', function() { | ||
setTimeout(reposition); | ||
}, true); | ||
// clean up any remaining handlers | ||
scope.$on('$destroy', function() { | ||
doc.off('keyup', closeOnEscape).off('click', closeOnClick); | ||
}); | ||
// close when pressing escape anywhere or clicking away | ||
angular.element(document) | ||
.on('keyup', closeOnEscape) | ||
.on('click', closeOnClick); | ||
scope.$on('$destroy', function() { | ||
angular.element(document) | ||
.off('keyup', closeOnEscape) | ||
.off('click', closeOnClick); | ||
}); | ||
function closeOnClick(e) { | ||
// close if target is not the trigger and is not in the popover | ||
var target = angular.element(e.target); | ||
var trigger = target.data('stackable-state'); | ||
if(scope.state !== trigger && target.closest(element).length === 0) { | ||
scope.state.show = false; | ||
scope.$apply(); | ||
} | ||
function closeOnClick(e) { | ||
// close if target is not in the popover | ||
var target = angular.element(e.target); | ||
if(target.closest(element).length === 0) { | ||
scope.state.show = false; | ||
scope.$apply(); | ||
} | ||
} | ||
function closeOnEscape(e) { | ||
if(scope.enableEscape && e.keyCode === 27) { | ||
e.stopPropagation(); | ||
scope.state.show = false; | ||
scope.$apply(); | ||
} | ||
function closeOnEscape(e) { | ||
if(scope.enableEscape && e.keyCode === 27) { | ||
e.stopPropagation(); | ||
scope.state.show = false; | ||
scope.$apply(); | ||
} | ||
} | ||
function reposition() { | ||
// resize popover content | ||
var content = element.find('.stackable-popover-content'); | ||
if(!content.length) { | ||
return; | ||
function reposition(content) { | ||
content.css('display', 'none'); | ||
var width = content.outerWidth(false); | ||
var height = content.outerHeight(false); | ||
// position popover content | ||
var position = {top: 0, left: 0}; | ||
var alignment = scope.alignment || 'center'; | ||
var placement = scope.placement || 'top'; | ||
if(placement === 'top' || placement === 'bottom') { | ||
// treat invalid 'top' or 'bottom' as 'center' | ||
if(['center', 'top', 'bottom'].indexOf(alignment) !== -1) { | ||
var triggerCenterX = (scope.state.position.left + | ||
scope.state.position.width / 2); | ||
position.left = triggerCenterX - width / 2; | ||
} else if(alignment === 'left') { | ||
position.left = scope.state.position.left; | ||
} else { | ||
// alignment 'right' | ||
position.left = (scope.state.position.left + | ||
scope.state.position.width - width); | ||
} | ||
content.css(extents); | ||
var height = content.outerHeight(true); | ||
var width = content.outerWidth(true); | ||
position.top = scope.state.position.top; | ||
if(placement === 'top') { | ||
position.top -= height; | ||
} else { | ||
position.top += scope.state.position.height; | ||
} | ||
} else { | ||
// else placement is 'left' or 'right' | ||
// position popover | ||
var position = {top: 0, left: 0}; | ||
var alignment = scope.alignment || 'center'; | ||
var placement = scope.placement || 'top'; | ||
if(placement === 'top' || placement === 'bottom') { | ||
// treat invalid 'top' or 'bottom' as 'center' | ||
if(['center', 'top', 'bottom'].indexOf(alignment) !== -1) { | ||
var triggerCenterX = (scope.state.position.left + | ||
scope.state.position.width / 2); | ||
position.left = triggerCenterX - width / 2; | ||
} else if(alignment === 'left') { | ||
position.left = scope.state.position.left; | ||
} else { | ||
// alignment 'right' | ||
position.left = (scope.state.position.left + | ||
scope.state.position.width - width); | ||
} | ||
// treat invalid 'left' or 'right' as 'center' | ||
if(['center', 'left', 'right'].indexOf(alignment) !== -1) { | ||
var triggerCenterY = (scope.state.position.top + | ||
scope.state.position.height / 2); | ||
position.top = triggerCenterY - height / 2; | ||
} else if(alignment === 'top') { | ||
position.top = scope.state.position.top; | ||
if(placement === 'top') { | ||
position.top -= height; | ||
} else { | ||
position.top += scope.state.position.height; | ||
} | ||
} else { | ||
// else placement is 'left' or 'right' | ||
// treat invalid 'left' or 'right' as 'center' | ||
if(['center', 'left', 'right'].indexOf(alignment) !== -1) { | ||
var triggerCenterY = (scope.state.position.top + | ||
scope.state.position.height / 2); | ||
position.top = triggerCenterY - height / 2; | ||
} else if(alignment === 'top') { | ||
position.top = scope.state.position.top; | ||
} else { | ||
// alignment 'bottom' | ||
position.top = (scope.state.position.top + | ||
scope.state.position.height - height); | ||
} | ||
position.left = scope.state.position.left; | ||
if(placement === 'left') { | ||
position.left -= width; | ||
} else { | ||
position.left += scope.state.position.width; | ||
} | ||
// alignment 'bottom' | ||
position.top = (scope.state.position.top + | ||
scope.state.position.height - height); | ||
} | ||
position.top += 'px'; | ||
position.left += 'px'; | ||
element.css(position); | ||
position.left = scope.state.position.left; | ||
if(placement === 'left') { | ||
position.left -= width; | ||
} else { | ||
position.left += scope.state.position.width; | ||
} | ||
} | ||
}; | ||
position.top += 'px'; | ||
position.left += 'px'; | ||
content.css(position); | ||
content.css('display', ''); | ||
} | ||
} | ||
@@ -421,9 +303,23 @@ } | ||
// toggle show on click | ||
element.on('click', function() { | ||
scope.$apply(function() { | ||
var toggleEvent = attrs.stackableToggle || 'click'; | ||
if(toggleEvent === 'hover') { | ||
// show on enter, hide on leave | ||
element.hover(function() { | ||
state.show = true; | ||
updateState(state); | ||
scope.$apply(); | ||
}, function() { | ||
state.show = false; | ||
updateState(state); | ||
scope.$apply(); | ||
}); | ||
} else { | ||
// default to click | ||
element.on('click', function(e) { | ||
e.stopPropagation(); | ||
state.show = !state.show; | ||
updateState(state); | ||
scope.$apply(); | ||
}); | ||
}); | ||
} | ||
@@ -444,3 +340,2 @@ function resized() { | ||
set(scope, state); | ||
element.data('stackable-state', state); | ||
} | ||
@@ -447,0 +342,0 @@ |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
0
21024
594