Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improved placement, individual duration, events and minor refactorings #15

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
10 changes: 7 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,12 @@ The `notify` function can either be passed a string or an object. When passing
* `classes` - Optional. A list of custom CSS classes to apply to the message element.
* `messageTemplate` - Optional. A string containing any valid Angular HTML which will be shown instead of the regular `message` text. The string must contain one root element like all valid Angular HTML templates (so wrap everything in a `<span>`).
* `scope` - Optional. A valid Angular scope object. The scope of the template will be created by calling `$new()` on this scope.
* `position` - Optional. Currently `center` and `right` are the only acceptable values.
* `position` - Optional. 'left', 'right' and 'center' are the only acceptable values.
* `duration` - Optional. The duration (in milliseconds) of the message. A duration of 0 will prevent the message from closing automatically.
* `container` - Optional. Element that contains each notification. Defaults to `document.body`.
* `onOpen` - Optional. Function that is called when the notification is shown to the user. The function receives the message of the notification as its only parameter.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think all 3 events need to take a better argument than the message string. I would think the actual return value (which is an object that contains a modifiable message property as well as a close method) would be more appropriate.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are absolutely right, that would make way more sense than just the message.

* `onClick` - Optional. Function that is called when the notification is clicked by the user. The function receives the message of the notification as its only parameter.
* `onClose` - Optional. Function that is called when the notification is closed by the user manually. It is not called when `closeAll()` is called and closes the notification programatically. The function receives the message of the notification as its only parameter.

This function will return an object with a `close()` method and a `message` property.

Expand All @@ -59,12 +63,12 @@ Call `config` to set the default configuration options for angular-notify. The
* `startTop` - The Y pixel value where messages will be shown.
* `verticalSpacing` - The number of pixels that should be reserved between messages vertically.
* `templateUrl` - The default message template.
* `position` - The default position of each message. Currently only `center` and `right` are the supported values.
* `position` - The default position of each message. 'left', 'right' and 'center' are the only acceptable values.
* `container` - The default element that contains each notification. Defaults to `document.body`.

### notify.closeAll()

Closes all currently open notifications.
Closes all currently open notifications. Notifications that are closed by `closeAll()` will not call their `onClose`-events.

## Providing Custom Templates

Expand Down
13 changes: 12 additions & 1 deletion angular-notify.css
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
.cg-notify-message {
position:fixed;
left:50%;
top:0px;
z-index: 9999;
max-width:400px;
Expand All @@ -23,6 +22,18 @@
box-shadow: 0 6px 12px rgba(0,0,0,.175);
}

.cg-notify-message-center {
left:50%;
}

.cg-notify-message-left {
left:1%;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can both this and the right class use a px left and right. 10 or15px most likely?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, I will fix that as soon as possible.

}

.cg-notify-message-right {
right:1%;
}

.cg-notify-message a {
font-weight:bold;
color:inherit;
Expand Down
2 changes: 1 addition & 1 deletion angular-notify.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<div class="cg-notify-message" ng-class="$classes">
<div class="cg-notify-message" ng-class="$classes" ng-click="$click()">

<div ng-show="!$messageTemplate">
{{$message}}
Expand Down
63 changes: 41 additions & 22 deletions angular-notify.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,30 @@ angular.module('cgNotify', []).factory('notify',['$timeout','$http','$compile','
args = {message:args};
}

args.templateUrl = args.templateUrl ? args.templateUrl : defaultTemplateUrl;
args.position = args.position ? args.position : position;
args.container = args.container ? args.container : container;
args.classes = args.classes ? args.classes : '';

var scope = args.scope ? args.scope.$new() : $rootScope.$new();
args.templateUrl = angular.isString(args.templateUrl) && args.templateUrl.length > 0 ? args.templateUrl : defaultTemplateUrl;
args.position = angular.isString(args.position) ? args.position : position;
args.container = angular.isObject(args.container) ? args.container : container;
args.classes = angular.isString(args.classes) ? args.classes : '';
args.duration = angular.isNumber(args.duration) ? args.duration : duration;
args.onOpen = angular.isFunction(args.onOpen) ? args.onOpen : undefined;
args.onClose = angular.isFunction(args.onClose) ? args.onClose : undefined;

var scope = angular.isDefined(args.scope) ? args.scope.$new() : $rootScope.$new();
scope.$message = args.message;
scope.$classes = args.classes;
scope.$messageTemplate = args.messageTemplate;
scope.$messageTemplate = args.messageTemplate;

if(angular.isFunction(args.onClick)){
scope.$click = function(){
args.onClick(scope.$message);
};
}

$http.get(args.templateUrl,{cache: $templateCache}).success(function(template){

var templateElement = $compile(template)(scope);
templateElement.bind('webkitTransitionEnd oTransitionEnd otransitionend transitionend msTransitionEnd', function(e){
if (e.propertyName === 'opacity' ||
if (e.propertyName === 'opacity' || e.currentTarget.style.opacity === '0' ||
(e.originalEvent && e.originalEvent.propertyName === 'opacity')){

templateElement.remove();
Expand All @@ -57,19 +66,26 @@ angular.module('cgNotify', []).factory('notify',['$timeout','$http','$compile','
angular.element(args.container).append(templateElement);
messageElements.push(templateElement);

if (args.position === 'center'){
$timeout(function(){
templateElement.css('margin-left','-' + (templateElement[0].offsetWidth /2) + 'px');
});
switch (args.position){
case 'center': templateElement.addClass('cg-notify-message-center');
templateElement.css('margin-left','-' + (templateElement[0].offsetWidth /2) + 'px');

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've found that wrapping this line in a $timeout call (as was being done previously) is actually important. Without that, the "center" case of positioning stopped working for me:

Before:

working

After:

not-working

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, I will take a look into that!

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, and thanks for this PR! I'm using it primarily because of the custom duration feature, but I certainly don't mind getting the other improvements for free as well :)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, fixed that part. Thanks for testing my PR! Please keep me up to date about other insufficiencies :)

break;
case 'left': templateElement.addClass('cg-notify-message-left');
break;
case 'right': templateElement.addClass('cg-notify-message-right');
break;
}

scope.$close = function(){
templateElement.css('opacity',0).attr('data-closing','true');
layoutMessages();

if(angular.isDefined(args.onClose)){
args.onClose(scope.$message);
}
};

var layoutMessages = function(){
var j = 0;
var currentY = startTop;
for(var i = messageElements.length - 1; i >= 0; i --){
var shadowHeight = 10;
Expand All @@ -82,18 +98,21 @@ angular.module('cgNotify', []).factory('notify',['$timeout','$http','$compile','
currentY += height + verticalSpacing;
}
element.css('top',top + 'px').css('margin-top','-' + (height+shadowHeight) + 'px').css('visibility','visible');
j ++;
}
};

$timeout(function(){
layoutMessages();
});

if (duration > 0){
if (args.duration > 0){
$timeout(function(){
scope.$close();
},duration);
},args.duration);
}

if(angular.isDefined(args.onOpen)){
args.onOpen(scope.$message);
}

}).error(function(data){
Expand Down Expand Up @@ -122,12 +141,12 @@ angular.module('cgNotify', []).factory('notify',['$timeout','$http','$compile','
};

notify.config = function(args){
startTop = !angular.isUndefined(args.startTop) ? args.startTop : startTop;
verticalSpacing = !angular.isUndefined(args.verticalSpacing) ? args.verticalSpacing : verticalSpacing;
duration = !angular.isUndefined(args.duration) ? args.duration : duration;
defaultTemplateUrl = args.templateUrl ? args.templateUrl : defaultTemplateUrl;
position = !angular.isUndefined(args.position) ? args.position : position;
container = args.container ? args.container : container;
startTop = angular.isNumber(args.startTop) ? args.startTop : startTop;
verticalSpacing = angular.isNumber(args.verticalSpacing) ? args.verticalSpacing : verticalSpacing;
duration = angular.isNumber(args.duration) ? args.duration : duration;
defaultTemplateUrl = angular.isString(args.templateUrl) && args.templateUrl.length > 0 ? args.templateUrl : defaultTemplateUrl;
position = angular.isString(args.position) ? args.position : position;
container = angular.isObject(args.container) ? args.container : container;
};

notify.closeAll = function(){
Expand Down
9 changes: 8 additions & 1 deletion demo/demo.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,18 @@ angular.module('app').controller('DemoCtrl',function($scope,notify){
$scope.msg = 'Hello! This is a sample message!';
$scope.template = '';

$scope.positions = ['center', 'left', 'right'];
$scope.position = $scope.positions[0];

$scope.duration = 10000;

$scope.demo = function(){
notify({
message: $scope.msg,
classes: $scope.classes,
templateUrl: $scope.template
templateUrl: $scope.template,
position: $scope.position,
duration: $scope.duration
});
};

Expand Down
15 changes: 14 additions & 1 deletion demo/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,20 @@ <h2>angular-notify</h2>
<div class="col-sm-8">
<input type="text" class="form-control" id="msg" placeholder="Message" ng-model="msg">
</div>
</div>
</div>
<div class="form-group">
<label for="position" class="col-sm-4 control-label">Position</label>
<div class="col-sm-8">
<select class="form-control" ng-model="position" ng-options="p for p in positions">
</select>
</div>
</div>
<div class="form-group">
<label for="duration" class="col-sm-4 control-label">Duration (ms)</label>
<div class="col-sm-8">
<input type="number" class="form-control" id="duration" placeholder="Duration in milliseconds" ng-model="duration">
</div>
</div>
<div class="form-group">
<label for="clz" class="col-sm-4 control-label">CSS Class</label>
<div class="col-sm-8">
Expand Down
13 changes: 12 additions & 1 deletion dist/angular-notify.css
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
.cg-notify-message {
position:fixed;
left:50%;
top:0px;
z-index: 9999;
max-width:400px;
Expand All @@ -23,6 +22,18 @@
box-shadow: 0 6px 12px rgba(0,0,0,.175);
}

.cg-notify-message-center {
left:50%;
}

.cg-notify-message-left {
left:1%;
}

.cg-notify-message-right {
right:1%;
}

.cg-notify-message a {
font-weight:bold;
color:inherit;
Expand Down
65 changes: 42 additions & 23 deletions dist/angular-notify.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,30 @@ angular.module('cgNotify', []).factory('notify',['$timeout','$http','$compile','
args = {message:args};
}

args.templateUrl = args.templateUrl ? args.templateUrl : defaultTemplateUrl;
args.position = args.position ? args.position : position;
args.container = args.container ? args.container : container;
args.classes = args.classes ? args.classes : '';

var scope = args.scope ? args.scope.$new() : $rootScope.$new();
args.templateUrl = angular.isString(args.templateUrl) && args.templateUrl.length > 0 ? args.templateUrl : defaultTemplateUrl;
args.position = angular.isString(args.position) ? args.position : position;
args.container = angular.isObject(args.container) ? args.container : container;
args.classes = angular.isString(args.classes) ? args.classes : '';
args.duration = angular.isNumber(args.duration) ? args.duration : duration;
args.onOpen = angular.isFunction(args.onOpen) ? args.onOpen : undefined;
args.onClose = angular.isFunction(args.onClose) ? args.onClose : undefined;

var scope = angular.isDefined(args.scope) ? args.scope.$new() : $rootScope.$new();
scope.$message = args.message;
scope.$classes = args.classes;
scope.$messageTemplate = args.messageTemplate;
scope.$messageTemplate = args.messageTemplate;

if(angular.isFunction(args.onClick)){
scope.$click = function(){
args.onClick(scope.$message);
};
}

$http.get(args.templateUrl,{cache: $templateCache}).success(function(template){

var templateElement = $compile(template)(scope);
templateElement.bind('webkitTransitionEnd oTransitionEnd otransitionend transitionend msTransitionEnd', function(e){
if (e.propertyName === 'opacity' ||
if (e.propertyName === 'opacity' || e.currentTarget.style.opacity === '0' ||
(e.originalEvent && e.originalEvent.propertyName === 'opacity')){

templateElement.remove();
Expand All @@ -57,19 +66,26 @@ angular.module('cgNotify', []).factory('notify',['$timeout','$http','$compile','
angular.element(args.container).append(templateElement);
messageElements.push(templateElement);

if (args.position === 'center'){
$timeout(function(){
templateElement.css('margin-left','-' + (templateElement[0].offsetWidth /2) + 'px');
});
switch (args.position){
case 'center': templateElement.addClass('cg-notify-message-center');
templateElement.css('margin-left','-' + (templateElement[0].offsetWidth /2) + 'px');
break;
case 'left': templateElement.addClass('cg-notify-message-left');
break;
case 'right': templateElement.addClass('cg-notify-message-right');
break;
}

scope.$close = function(){
templateElement.css('opacity',0).attr('data-closing','true');
layoutMessages();

if(angular.isDefined(args.onClose)){
args.onClose(scope.$message);
}
};

var layoutMessages = function(){
var j = 0;
var currentY = startTop;
for(var i = messageElements.length - 1; i >= 0; i --){
var shadowHeight = 10;
Expand All @@ -82,18 +98,21 @@ angular.module('cgNotify', []).factory('notify',['$timeout','$http','$compile','
currentY += height + verticalSpacing;
}
element.css('top',top + 'px').css('margin-top','-' + (height+shadowHeight) + 'px').css('visibility','visible');
j ++;
}
};

$timeout(function(){
layoutMessages();
});

if (duration > 0){
if (args.duration > 0){
$timeout(function(){
scope.$close();
},duration);
},args.duration);
}

if(angular.isDefined(args.onOpen)){
args.onOpen(scope.$message);
}

}).error(function(data){
Expand Down Expand Up @@ -122,12 +141,12 @@ angular.module('cgNotify', []).factory('notify',['$timeout','$http','$compile','
};

notify.config = function(args){
startTop = !angular.isUndefined(args.startTop) ? args.startTop : startTop;
verticalSpacing = !angular.isUndefined(args.verticalSpacing) ? args.verticalSpacing : verticalSpacing;
duration = !angular.isUndefined(args.duration) ? args.duration : duration;
defaultTemplateUrl = args.templateUrl ? args.templateUrl : defaultTemplateUrl;
position = !angular.isUndefined(args.position) ? args.position : position;
container = args.container ? args.container : container;
startTop = angular.isNumber(args.startTop) ? args.startTop : startTop;
verticalSpacing = angular.isNumber(args.verticalSpacing) ? args.verticalSpacing : verticalSpacing;
duration = angular.isNumber(args.duration) ? args.duration : duration;
defaultTemplateUrl = angular.isString(args.templateUrl) && args.templateUrl.length > 0 ? args.templateUrl : defaultTemplateUrl;
position = angular.isString(args.position) ? args.position : position;
container = angular.isObject(args.container) ? args.container : container;
};

notify.closeAll = function(){
Expand All @@ -145,7 +164,7 @@ angular.module('cgNotify').run(['$templateCache', function($templateCache) {
'use strict';

$templateCache.put('angular-notify.html',
"<div class=\"cg-notify-message\" ng-class=\"$classes\">\n" +
"<div class=\"cg-notify-message\" ng-class=\"$classes\" ng-click=\"$click()\">\n" +
"\n" +
" <div ng-show=\"!$messageTemplate\">\n" +
" {{$message}}\n" +
Expand Down
2 changes: 1 addition & 1 deletion dist/angular-notify.min.css

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading