PDA

View Full Version : Ext.ux.Notify



ry.extjs
31 Mar 2009, 12:10 PM
UPDATED 4/3/2009: Final version. Added support for more options. Added LGPL license. Updated demo. Notification windows can now be positioned on the top or bottom, left, center or right. When the browser window is resized, notifications will maintain proper positioning.

I needed a notification system for an application that I'm building, so I created this. This extension is based upon the work from efattal (http://extjs.com/forum/showthread.php?t=32365) in his Ext.ux.Notification extension. I felt his extension was lacking in some areas and the code needed some cleanup. First, I didn't like the notification windows sliding in from on the screen. It just looked weird. So, these notification windows slide in from the bottom of the screen. Also, new notifications come in from the bottom, sliding older notifications up. The notifications then fade out after a specified amount of time. Another change is the ability to use a message icon within the message body.

DEMO: http://www.ann0yanc3.com/Ext.ux.Notify/

USAGE: Click anywhere in the browser window to display a new notification.

PLEASE NOTE: I'm currently trying to find the source of an issue in Firefox with the fading out of the notification windows. It seems as though if you add several notifications quickly, the DelayedTask does not run on time. If you can help find the problem, that would be awesome.

Here's the extension code:



/**
* Ext.ux.Notify
* @version 1.0
* Copyright(c) 2009 nXgen Web Solutions
* http://www.nxgenwebsolutions.com
*
* ---------------------------------------------------------------------------
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation, either
* version 3 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see http://www.gnu.org/licenses/.
*
* ---------------------------------------------------------------------------
*
* This extension is based on the work of efattal from the ExtJS forums.
* http://extjs.com/forum/showthread.php?t=32365
*
* This extension provides an easy and simple notification system for web
* applications. It supports a notification title and message with optional
* icons associated with each. Please read through the configuration options
* in Ext.ux.NotifyMgr and Ext.ux.Notify to learn how to work with this extension.
*
* To create a new notification window, use the following code:
*
* new Ext.ux.Notify({
* title: 'Hi',
* msg: 'Hello, world!'
* }).show(document);
*
* The above code will create a notification window that is 200px wide and
* fades out in 2 seconds.
*
* Please note: You must set 'overflow:hidden' to the body element if you are
* bottom aligning the notifications to prevent scrollbars from being created.
*
*/
// create namespaces for the notify extension
Ext.namespace('Ext.ux.Notify');
Ext.namespace('Ext.ux.NotifyMgr');
/**
* @object Ext.ux.NotifyMgr
* This objects stores configuration options and temporary data for the notifications.
*/
Ext.ux.NotifyMgr = {
/**
* @cfg {String} msgIconBaseCls
* The base class to apply to the message icon. By default, a base class will not
* be applied.
*/
msgIconBaseCls: '',
/**
* @cfg {String} msgStyle
* Custom CSS styles to be applied to the notification message body in the format
* expected by {@link Ext.Element#applyStyles}.
*/
msgStyle: 'padding:10px',
/**
* @cfg {String} alignment
* The alignment of the notification window.
*
* Possible values:
* top-left
* top-center
* top-right
* bottom-left
* bottom-center
* bottom-right (default)
*/
alignment: 'bottom-right',
/**
* @cfg {Array} offsets
* An array (2 numbers) of offsets to be applied notifications after alignment.
* The first number is the offset from the top/bottom. The second is the offset from
* the left/right (does not apply to center aligned notifications). Defaults to [10, 10].
*/
offsets: [10, 10],
/**
* @cfg {Number} spacing
* The height in pixels of the space between each notification window. Defaults to 10.
*/
spacing: 10,
/**
* @cfg {Number} zIndex
* The z-index to apply to notification windows.
*/
zIndex: 30000,
// internal use only, do not modify
positions: [],
// internal use only, do not modify
windows: []
};
/**
* Monitor window size and adjust notification window positions, if necessary.
*/
Ext.EventManager.on(window, 'resize', function(){
var b = Ext.getBody(),
bw = b.getComputedWidth(),
bh = b.getComputedHeight();
if (!Ext.ux.NotifyMgr.windows.length) return;
for (var i = 0; i < Ext.ux.NotifyMgr.windows.length; ++i) {
var w = Ext.ux.NotifyMgr.windows[i].getInnerWidth() + Ext.ux.NotifyMgr.windows[i].getFrameWidth(),
h = Ext.ux.NotifyMgr.windows[i].getInnerHeight() + Ext.ux.NotifyMgr.windows[i].getFrameHeight();
switch (Ext.ux.NotifyMgr.alignment) {
case 'top-center':
Ext.ux.NotifyMgr.windows[i].el.setX(bw - parseInt(bw / 2) - parseInt(w / 2));
break;
case 'top-right':
Ext.ux.NotifyMgr.windows[i].el.setX(bw - w - (Ext.ux.NotifyMgr.offsets[1] || 10));
break;
case 'bottom-left':
var offset = 0;
for (var j = i + 1; j < Ext.ux.NotifyMgr.windows.length; ++j) {
offset += Ext.ux.NotifyMgr.windows[j].getInnerHeight() + Ext.ux.NotifyMgr.windows[j].getFrameHeight() + (Ext.ux.NotifyMgr.spacing || 10);
}
Ext.ux.NotifyMgr.windows[i].el.setY(bh - h - offset - (Ext.ux.NotifyMgr.offsets[0] || 10));
break;
case 'bottom-center':
var offset = 0;
for (var j = i + 1; j < Ext.ux.NotifyMgr.windows.length; ++j) {
offset += Ext.ux.NotifyMgr.windows[j].getInnerHeight() + Ext.ux.NotifyMgr.windows[j].getFrameHeight() + (Ext.ux.NotifyMgr.spacing || 10);
}
Ext.ux.NotifyMgr.windows[i].el.setY(bh - h - offset - (Ext.ux.NotifyMgr.offsets[0] || 10));
Ext.ux.NotifyMgr.windows[i].el.setX(bw - parseInt(bw / 2) - parseInt(w / 2));
break;
case 'bottom-right':
var offset = 0;
for (var j = i + 1; j < Ext.ux.NotifyMgr.windows.length; ++j) {
offset += Ext.ux.NotifyMgr.windows[j].getInnerHeight() + Ext.ux.NotifyMgr.windows[j].getFrameHeight() + (Ext.ux.NotifyMgr.spacing || 10);
}
Ext.ux.NotifyMgr.windows[i].el.setY(bh - h - offset - (Ext.ux.NotifyMgr.offsets[0] || 10));
Ext.ux.NotifyMgr.windows[i].el.setX(bw - w - (Ext.ux.NotifyMgr.offsets[1] || 10));
break;
}
}
});
/**
* @class Ext.ux.Notify
* @extends Ext.Window
* This class defines a notification.
*/
Ext.ux.Notify = Ext.extend(Ext.Window, {
/**
* @cfg {Number} msgWidth
* The width in pixels of the notification window. This value defaults to 250.
*/
/**
* @cfg {Number} hideDelay
* The length of time (in milliseconds) before a notification will fade out.
*/
/**
* @cfg {String} titleIconCls
* The CSS class for the notification title icon. This is equivalent to the iconCls
* option of a normal {@link Ext.Window}.
*/
/**
* @cfg {String} msgIconCls
* The CSS class for the notification message icon.
*/
initComponent: function(){
Ext.apply(this, {
shadow:false,
draggable:false,
closable:false,
resizable:false,
iconCls: this.titleIconCls || '',
bodyStyle: Ext.ux.NotifyMgr.msgStyle
});
this.task = new Ext.util.DelayedTask(this.hide, this);
if (this.msgIconCls && this.msgIconCls !== '') {
var baseCls = Ext.ux.NotifyMgr.msgIconBaseCls !== '' ? Ext.ux.NotifyMgr.msgIconBaseCls + ' ' : '';
this.html = '<div class="'+baseCls+this.msgIconCls+'"></div>' + this.msg;
} else {
this.html = this.msg;
}
Ext.ux.Notify.superclass.initComponent.call(this);
},
onDestroy: function(){
Ext.ux.NotifyMgr.positions.remove(this.pos);
Ext.ux.NotifyMgr.windows.remove(this);
Ext.ux.Notify.superclass.onDestroy.call(this);
},
afterShow: function(){
if(Ext.isMac && Ext.isGecko){ // work around stupid FF 2.0/Mac scroll bar bug
this.cascade(this.setAutoScroll);
}
if(this.layout){
this.doLayout();
}
if(this.keyMap){
this.keyMap.enable();
}
this.fireEvent("show", this);
if (this.closable) {
this.on('close', function(){
Ext.ux.NotifyMgr.positions.remove(this.pos);
Ext.ux.NotifyMgr.windows.remove(this);
this.task.cancel();
}, this);
}
this.task.delay(this.hideDelay || 3000);
},
animShow: function(){
this.pos = 0;
while (Ext.ux.NotifyMgr.positions.indexOf(this.pos) >- 1) this.pos++;
Ext.ux.NotifyMgr.positions.push(this.pos);
this.setZIndex(Ext.ux.NotifyMgr.zIndex);
this.el.setXY([-9999, -9999]);
this.el.show();
this.msgWidth = (this.msgWidth && this.msgWidth > 0) ? this.msgWidth : 250;
this.setWidth(this.msgWidth);
var bh = Ext.getBody().getComputedHeight(),
bw = Ext.getBody().getComputedWidth(),
h = this.getInnerHeight() + this.getFrameHeight(),
w = this.getInnerWidth() + this.getFrameWidth(),
x, y, shift;
switch (Ext.ux.NotifyMgr.alignment) {
case 'top-left':
x = Ext.ux.NotifyMgr.offsets[1] || 10;
y = -h;
shift = 0;
break;
case 'top-center':
x = bw - parseInt(bw / 2) - parseInt(w / 2);
y = -h;
shift = 0;
break;
case 'top-right':
x = bw - w - (Ext.ux.NotifyMgr.offsets[1] || 10);
y = -h;
shift = 0;
break;
case 'bottom-left':
x = Ext.ux.NotifyMgr.offsets[1] || 10;
y = bh;
shift = bh - h;
break;
case 'bottom-center':
x = bw - parseInt(bw / 2) - parseInt(w / 2);
y = bh;
shift = bh - h;
break;
case 'bottom-right':
x = bw - w - (Ext.ux.NotifyMgr.offsets[1] || 10);
y = bh;
shift = bh - h;
break;
}
this.el.setXY([x, y]);
this.el.shift({
y: shift - (Ext.ux.NotifyMgr.alignment.indexOf('bottom') > -1 ? Ext.ux.NotifyMgr.offsets[0] : -Ext.ux.NotifyMgr.offsets[0]),
duration: 1,
callback: this.afterShow,
scope: this
});
if (Ext.ux.NotifyMgr.windows.length > 0) {
for (var i = 0; i < Ext.ux.NotifyMgr.windows.length; ++i) {
var offset = 0;
for (var j = i; j < Ext.ux.NotifyMgr.windows.length; ++j) {
offset += Ext.ux.NotifyMgr.windows[j].getInnerHeight() + Ext.ux.NotifyMgr.windows[j].getFrameHeight() + (Ext.ux.NotifyMgr.spacing || 10);
}
Ext.ux.NotifyMgr.windows[i].el.shift({
concurrent: true,
y: shift - (Ext.ux.NotifyMgr.alignment.indexOf('bottom') > -1 ? offset : -offset) - (Ext.ux.NotifyMgr.alignment.indexOf('bottom') > -1 ? Ext.ux.NotifyMgr.offsets[0] : -Ext.ux.NotifyMgr.offsets[0]),
duration:1
});
}
}
Ext.ux.NotifyMgr.windows.push(this);
},
animHide: function(){
Ext.ux.NotifyMgr.positions.remove(this.pos);
Ext.ux.NotifyMgr.windows.remove(this);
this.el.fadeOut({
duration: 2,
scope: this,
block: true,
stopFx: true,
useDisplay:false,
callback: this.destroy
});
}
});


You'll notice that I don't call the superclass afterShow() method. This is because I didn't want to bring the window to the front and give it focus.

Here's the code to display a new notification:



new Ext.ux.Notify({
title: 'Notification title',
msg: 'Notification message.'
}).show(document);


All options are explained in the source file. Please read through it.

Please let me know what you think of this extension, or if you have any questions, you find any bugs, or would like to see a feature in it.

mjlecomte
31 Mar 2009, 2:04 PM
Nice implementation, thanks for sharing.

donssmith
31 Mar 2009, 10:07 PM
Very nice. A couple of suggestions...

1. It would be nice if the message didn't fade away while the mouse is hover over it... the way Outlook works.
2. It would be nice to be able to hook into a click event when the message is clicked... again similar to Outlook

galdaka
31 Mar 2009, 10:43 PM
Hi,

not work in IE7. but perfect in FF3.

Greettings,

mjlecomte
1 Apr 2009, 3:08 AM
Very nice. A couple of suggestions...

1. It would be nice if the message didn't fade away while the mouse is hover over it... the way Outlook works.
2. It would be nice to be able to hook into a click event when the message is clicked... again similar to Outlook

You might want to check the implementation in my signature for those features.

ry.extjs
1 Apr 2009, 9:24 AM
Very nice. A couple of suggestions...

1. It would be nice if the message didn't fade away while the mouse is hover over it... the way Outlook works.
2. It would be nice to be able to hook into a click event when the message is clicked... again similar to Outlook

This extension is meant to be a very simple notification system. Please refer to mjlecomte's implementation for those features. Thanks for you input.

ry.extjs
1 Apr 2009, 9:29 AM
Hi,

not work in IE7. but perfect in FF3.

Greettings,

IE7 didn't like catching the 'click' event on the window object. I changed it to the document instead. It should work now. Although, I'm trying to pinpoint an error with the shim right now.

EDIT: Found out that when the notification window is closable, IE7 freaks out looking for a shim when it's being destroyed. I can't pinpoint the exact line of code yet. For now, I've removed the ability to make the notification window closable in the demo.

ry.extjs
2 Apr 2009, 10:21 PM
BUMP. Updated to final version.