Sunday, October 31, 2010

JQuery UI Widget–Panel

I’ve been trying to get better at following the JQuery UI Theme styles – the fact of the matter is I’ve just never been great at writing fancy style sheets, so having a re-usable framework complete with fancy gradient backgrounds is awesome. However, I’ve always been frustrated by the lack of a good “container” skeleton widget in the standard toolkit. I decided to spend a little bit of time designing a panel widget that works on items with this structure:

<div><h4>{title}</h4>{content}</div>

Let me know what you think!


You'll need these styles defined somewhere (I use Styles.css):
.ui-panel div.ui-panel-header
{
padding: 4px 8px 2px 12px;
height: 22px;
}
.ui-panel a.ui-panel-header
{
float: right;
}
.ui-panel div.ui-panel-content
{
padding: 4px 4px 4px 4px;
}


$.widget('ui.panel', {


options: { 


    collapsable: true,


    collapsed: false,


    title: null,


    barHeight: 29


}, defaults: {


    headerCss: 'ui-widget-header ui-corner-top ui-panel-header'


  , contentsCss: 'ui-widget-content ui-corner-bottom ui-panel-content ui-helper-clearfix'


  , panelCss: 'ui-widget ui-panel'


  , showSpeed: 'slow'


  , titleSelector: 'h1,h2,h3,h4,h5'


  , titleTemplate: '<span />'


}, _create: function () {


    var buffer;


    var self = this;


    self.initialState = { };


    // Parse attribute options


    if (self.element.attr('collapsable') == 'false')


        self.options.collapsable = false;


    if (self.element.attr('collapsed') == 'true')


        self.options.collapsable = true;    


    // Create the header div


    self.header = $('<div class="' + self.defaults.headerCss + '" />');


    // Look for an h element to rip out for the title.  If it is a h1 or h2, increase the bar's height


    self.initialState.header = self.element.children(self.defaults.titleSelector).first();


    if (self.initialState.header.length == 1) {


        self.initialState.header.remove();


        if (self.initialState.header[0].tagName == 'H1' || self.initialState.header[0].tagName == 'H2'


         && self.options.barHeight == 29)


             self.options.barHeight = 49;


        if (self.options.title == null)


            self.options.title = self.initialState.header.text();


    }    


    buffer = $(self.defaults.titleTemplate);


    if (self.options.title != null)


        buffer.text(self.options.title);


    self.header.append(buffer);    


    // TODO: Create the collapsable div and wire it up


    if (self.options.collapsable) {


        buffer = $("<a class='ui-panel-header ui-icon ui-icon-circle-triangle-n' href='javascript:void(0)'></a>");


        buffer.bind('click', function() { self._trigger('toggle'); });


        self.header.append(buffer);


    }


 


    // Create the content element and move original content into it.


    self.contents = $('<div class="' + self.defaults.contentsCss + '" />');


    self.contents.text(self.element.text());


    self.element.text('');


    self.element.children().appendTo(self.contents);


    // Finally, add my panel class and throw everything into the document.


    self.element.addClass(self.defaults.panelCss);


    self.element.append(self.header);


    self.element.append(self.contents);


    self.element.bind('paneltoggle', 


    function(event) {


        if (self.options.collapsed)


            self.expand();


        else


            self.collapse();


        return true;


    });


    self.element.bind('panelcollapse', 


    function(event) {          


        if (self.options.collapsed == false) {


            self.contents.hide(self.defaults.showSpeed, 


            function() {


                self.header.children('a').removeClass('ui-icon-circle-triangle-n')


                       .addClass('ui-icon-circle-triangle-s');


            });


            self.options.collapsed = true;


        }


        return true;


    });


    self.element.bind('panelexpand', 


    function(event) {


    if (self.options.collapsed) {


            self.contents.show(self.defaults.showSpeed, 


            function() {


                self.header.children('a').removeClass('ui-icon-circle-triangle-s')


                       .addClass('ui-icon-circle-triangle-n');


            });


            self.options.collapsed = false;


        }


        return true;


    });


}, expand: function () {


    this._trigger('expand');


}, collapse: function() {


    this._trigger('collapse');


}, destroy: function () {


    this.element.unbind('panelexpand panelcollapse');


    // Return element to pre-widget state


    this.header.remove();


    this.contents.detach();


    this.element.text(this.contents.text());


    if (this.initialState.header != null)


        this.element.append(this.initialState.header);    


    this.element.append(this.contents.children());


    // Call base destructor    


    $.Widget.prototype.destroy.apply(this, arguments); 


}});

$.extend($.ui.panel, { version: '@VERSION') });