I have a view in my Ext JS 4 app that has a grid panel which fills the viewport. I wanted some notification UI for success/failure upon save or other actions but not a modal dialog and not the notification window user extension (which I think looks great). Instead, I wanted a “bar” that would go across the page at the top. I’ve seen it all over the place recently. The way I ended up implementing it was to use a toolbar. This is what it looks like:
The GridBar code is pretty straightforward. I have a small animation to fade the bar in, but haven’t done anything to fade it out.
Ext.define('MyStuff.GridBar', { extend: 'Ext.toolbar.Toolbar', alias: 'widget.gridbar', requires: [ 'Ext.toolbar.*' ], cls: 'gridbar', initComponent : function() { this.items = [ { itemId: 'msg', xtype: 'tbtext', text: '' }, '->', { text: 'X', scope: this, handler: this.hideBar } ] this.callParent(arguments); this.msgItem = this.child('#msg'); }, showSuccess : function(msg) { this.getEl().setOpacity(0.25, false); this.addClass('gridbar-success'); this.showBar(msg); }, showError : function(msg) { this.getEl().setOpacity(0.25, false); this.addClass('gridbar-error'); this.showBar(msg); }, showBar : function(msg) { this.msgItem.update(msg); this.show(); this.getEl().fadeIn({opacity: 1, duration: 1000}); this.ownerCt.forceComponentLayout(); }, hideBar : function () { if (!this.isHidden()) { this.hide(); this.ownerCt.forceComponentLayout(); this.removeCls("gridbar-success"); this.removeCls("gridbar-error"); this.msgItem.update(''); } }, // Add custom processing to the onRender phase. afterRender: function() { this.callParent(arguments); this.hide(); } });
I used the following css:
.gridbar { margin: 3px !important; } .gridbar-success, .x-nlg .gridbar-success { border: 1px solid #AEE0AE !important; background-color: #D1EED1 !important; background-image: none !important; } .gridbar-error, .x-nlg .gridbar-error { border: 1px solid #F79897 !important; background-color: #FDDFDE !important; background-image: none !important; }
The way to use it is to just add the GridBar into the docked items. Like this:
{ xtype: 'grid', itemId: 'myGrid', // more grid configs columns : [ // column definitions here ], dockedItems : [ { itemId: 'gridBar', xtype: 'gridbar', dock: 'top' }, { xtype: 'toolbar', dock: 'top', items: [ // put in whatever items you have here ] } ] };
By default, the GridBar will be hidden on render. I had to call forceComponentLayout whenever hiding and showing the GridBar to get the UI to look correct. I tried many things to not call it, but that was the only thing that seemed to work.
Ext 4.1.0 Update
I revisited my “notification bar” with the release of Ext JS 4.1.0, and I was able to streamline it a little bit. Most importantly, I was able to remove the forceComponentLayout. Here is this code. I updated the name to be a little more generic, from GridBar to NotifyBar. Usage is the same as above.
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
.notifybar{ | |
margin: 3px !important; | |
} | |
.notifybar-success, .x-nlg .notifybar-success { | |
border: 1px solid #AEE0AE !important; | |
background-color: #D1EED1 !important; | |
background-image: none !important; | |
} | |
.notifybar-error, .x-nlg .notifybar-error { | |
border: 1px solid #F79897 !important; | |
background-color: #FDDFDE !important; | |
background-image: none !important; | |
} | |
.x-toolbar .notifybar-text { | |
white-space: normal !important; | |
} |
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
/* | |
Copyright (c) 2013 Ann Huddleston | |
Permission is hereby granted, free of charge, to any person obtaining a copy | |
of this software and associated documentation files (the "Software"), to deal | |
in the Software without restriction, including without limitation the rights | |
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
copies of the Software, and to permit persons to whom the Software is | |
furnished to do so, subject to the following conditions: | |
The above copyright notice and this permission notice shall be included in | |
all copies or substantial portions of the Software. | |
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
THE SOFTWARE. | |
*/ | |
Ext.define('MyStuff.NotifyBar', { | |
extend: 'Ext.toolbar.Toolbar', | |
alias: 'widget.notifybar', | |
requires: [ | |
'Ext.toolbar.*' | |
], | |
cls: 'notifybar', | |
initComponent : function() { | |
this.items = [ | |
{ | |
itemId: 'msg', | |
flex: 1, | |
xtype: 'tbtext', | |
cls: 'notifybar-text', | |
text: '' | |
}, | |
{ | |
text: 'X', | |
scope: this, | |
handler: this.hideBar | |
} | |
]; | |
this.callParent(arguments); | |
this.msgItem = this.child('#msg'); | |
}, | |
showSuccess : function(msg) { | |
this.addClass('notifybar-success'); | |
this.showBar(msg); | |
}, | |
showError : function(msg) { | |
this.addClass('notifybar-error'); | |
this.showBar(msg); | |
}, | |
showBar : function(msg) { | |
this.msgItem.setText(msg); | |
this.show(); | |
this.getEl().setOpacity(1, { | |
duration: 1000 | |
}); | |
}, | |
hideBar : function () { | |
if (this.rendered && !this.isHidden()) { | |
Ext.suspendLayouts(); | |
this.hide(); | |
this.getEl().setOpacity(0.25, false); | |
this.removeCls("notifybar-success notifybar-error"); | |
this.msgItem.update(''); | |
Ext.resumeLayouts(true); | |
} | |
}, | |
// Add custom processing to the beforeRender phase. | |
beforeRender: function() { | |
this.callParent(arguments); | |
this.hide(); | |
}, | |
onDestroy: function() { | |
this.getEl().stopAnimation(); | |
this.callParent(arguments); | |
} | |
}); |