Yosef Meller

NAME

HTML::Widget::SideBar - Creates the HTML (and possibly some Javascript) for a navigational or otherwise active (hierarchical) sidebar for a web page.

SYNOPSYS

 use HTML::Widget::SideBar;
 use CGI; # Or something like that.

 # We are going to create a sidebar in which only the active (clicked) branch
 # is visible.
 my $tree = HTML::Widget::SideBar->new;
 $tree->setToggleAction;

 foreach (1..3) {
     my $list = $tree->append(value => "list$_");
     $list->append(value => "aaa$_", URL => "http://localhost/$_");
     $list->append(value => "bbb$_");
     $list->append(value => "ccc$_");
 }
 $tree->getSubTree(3)->setActive;

 print header, start_html(-style => $tree->buildCSS($tree->deepBlueCSS), 
                          -script => $tree->baseJS);
 print join "\n", $tree->getHTML(styles => {bar => 'nav',
                                            level0 => 'navlink',
                                            level0Over => 'navover'},
                                 expand => 1
                                 );
 print end_html;

DESCRIPTION

HTML::Widget::SideBar creates the HTML, and possibly some Javascript and CSS for a hirarchical side menu bar. It is very flexible, and allows you to create both simple navigational menus and complex dynamic sidebars with Javascript actions associated with items on the menu.

This module started as a hack on my Javascript::Menu, which makes them very similar, so if you got one of them, you'll use the other with no sweat, I think.

The module supports the notion of an 'active item' (usually the item denoting the page the user is viewing) and gives such item special treatment by marking it with a special CSS class and making it visible initially. It also has special support for selection menus where opening a branch closes all others.

What should you expect to see?

This depends greatly on your style definitions and action assignment (if you use that feature). Basically you'll have a vertical bar (which will take up as much of the screen as your CSS will allow). Inside that bar you'll have a tree of nested lists, and you can define the style for each level. When an item is clicked - its action is performed. A special predefined action allows you to show/hide child lists.

By default only the active branch (the branch containing the active item) and the top level list will be visible. You can override this (see getHTML).

Some naming rules

The sidebar will get an HTML id attribute. The default is 'sidebar' but this is changeable through getHTML, as other naming rules.

Every list will be of class 'list' unless another class is given through getHTML.

Every item in every list will be of the same class as all other items on the same level. The default is 'item' for all items, but you can set each level separately through getHTML.

The active item's class name is its level's class name, appended with 'Active'.

Optionally, you may also set a mouseover style. For those of you who design for Mozilla, you really don't need that, just use the CSS pseudo-class :hover. For others, this will set the onMouseOver and onMouseOut attributes of an item to switch to and from that class.

Setting up the supporting code.

The sidebar created by this menu is formatted by CSS only. This means you'll have to supply it. I included a class method called buildCSS which takes a datastructure (described below) and turns it into CSS, and an example of a sidebar design in such datastructure (I used this design in production).

You may also want to use the toggling support (described below), and in this case you'll need some Javascript. This is given directly through baseJS. You can use it straight or dump to a file and tweak it to suit you best.

But what are these actions and how do I generate them?

An action is basically a piece of Javascript code that is executed when the user clicks on an item. It is added to the onClick attribute of the item. However, actions in this module are not plain strings. Instead, an action is a subroutine reference that is called when the item's HTML code is being processed. It does what it does, then returns a string containing the Javascript code. In order for this sub to be able to do anything useful, it gets 3 arguments passed to it:

  1. A reference to the node being processed, so you can get information on the node via object methods.

  2. The item's level in the hierarchy - the main menu is at level 0, the caption is at level -1.

  3. The menu's unique suffix.

To make an item do nothing at all, use $item->setAction with no parameters.

An important feature is that actions are inherited by new nodes from their parents. This allows you to set the beaveiour when you create the root element and not worry about it later on.

I18n alert! What this all means is that you supply some of the strings the module will be working with. This means you could, by mistake, send strings that are mixed utf8 (perl's internal encoding) and your encoding. This might break things, so if something breaks, see that your strings are in one encoding. A bitch, eh? That's the way it is when you're not in an English-speaking country.

But I don't need all this stuff! I just want a navigational menu!

Cool. Just set the URL property of an object either in the constructor call or using setURL. Menu items will be created with that URL. You can also combine a URL with an action.

Toggling a menu on/off.

As a convenience, a stock action is supplied that closes any list but the one clicked, and shows the clicked item's sublist if it has any. You can set this action on an item by the setToggleAction method.

Building the tree

To get the tree that represents the structure of the sidebar, you have 3 ways:

The hard way: HTML::Widget::SideBar->new

This builds the root node, with your desired value and action, URL or both (which will be the default for all children of this node). You add nodes with $tree->append, and descend the hierarchy using methods found in the parent class - Tree::Numbered. For each element you supply the value (what is shown on the screen) and possibly an action.

The easier way: HTML::Widget::SideBar->convert

This just takes an existing Tree::Numbered and blesses it as a SideBar, adding an action to each node. This is easier if you already have the data structure for something else, and you want to make a menu out of it.

A nice shortcut: HTML::Widget::SideBar->readDB

If you have the module Tree::Numbered::DB (another one of mine) and you use it to store trees in a database, this method allows you to read such table directly and convert it to a menu. This is extremely useful, trust me :)

Printing the code

Now all you have to do is $tree->getHTML. this will return an array so you can shift out the caption and locate it inside some div while the rest of the menu is located outside, avoiding width constraints. You can also push other stuff inside and create a widget for your script.

At any time you can also call $tree->getScript which will get you any Javascript code that was generated for the script. You won't get that code in the HTML array, which is new in version 1.02.

Calling any of the above methods after changing the tree (setting a field or adding a node but still not deleting one) will regenerate both the HTML and the script! Pay attention to their concurrency.

METHODS

This section only describes methods that are not the same as in Tree::Numbered. Obligatory arguments are marked.

Constructors

There are three of them:

new (value => $value, action => $action, URL => $url, render_hidden => $yes_or_not)

Creates a new tree with one root element, whose text is specified by the value argument. If an action is not supplied, the package's default do-nothig action will be used. You'll have to add nodes manually via the append method.

If a URL is supplied, the node will be an anchor reffering to that URL.

If render_hidden is false, no sub-menus will appear unless the active item is inside a sub menu, in which case said sub-menu will appear.

convert (tree => $tree, action => $action, base_URL => $url)

Converts a tree (given in the tree argument) into an instance of Javascript::Menu. You will lose the original tree of course, so if you still need it, first use $tree->clone (see Tree::Numbered).

Giving a value to base_URL will copy that value to the URL field of every node in the tree. you can add to this using deepProcess.

As in new, if action is not specified, one will be created for you.

readDB (source_name => $table, source => $dbh, cols => $cols, action => $action, URL_col => $urlcol);

Creates a new menu from a table that contains tree data as specified in Tree::Numbered::DB. Arguments are the same as to new, except for the required source_name, which specifies the name of the table to be read, and source, which is a DBI database handle.

The cols argument allows you to supply field mappings for the tree (see Tree::Numberd::DB). URL_col is a shortcut for giving a mapping to a collumn containing the URLs of nodes (if that's what you need). If you provide this argument, it will override any collision in the $cols hashref.

append (value => $value, action => $action, URL => $url, render_hidden => $yes_or_not)

Adds a new child with the value (caption) $value. An action or a URL are optional, as described in new. If one of those is not given, the value is taken from its parent (if its parent has one).

getHTML (styles => $styles, caption => 'altCaption', no_ie => true, expand => false)

This method returns the HTML for a sidebar whose caption is the node the method was invoked on. The sidebar's caption will be the root element's value unless the caption argument is given. All arguments are optional if the tree was not changet since the last call to either getHTML or getScript.

The expand argument, if true, will cause all nodes to be shown. Otherwise only the active branch us shown.

Unless you specify no_ie as true, the sidebar's caption will be wrapped up in a link so you cn use the :hover CSS pseudo-class on it.

the optional styles argument allows you to change default style names described above. This should be a hash reference, with a key for each style, specifying the new name. Like:

 styles => {bar => 'nav',
            level0 => 'navlink',
            level0Over => 'navover'}

You can give values to 'bar' (sidebar name), 'list' (list style) and 'item' (list item class). You can also give a certain level N its own class by giving a pair: levelN => 'someclass'. A hover style for this property can be given either with 'levelNOver', or the level's class appended with 'Over'. The first level is level 0. if a style called 'itemOver' is given, it will apply to all items regardless of their class, unless they already have some other mouseover setting.

In aray context will return the array as said, in scalar context will return a ref to that array.

getScript (styles => $styles, caption => 'altCaption', no_ie => true, expand => false)

Returns the script generated for the sidebar. All arguments are ignored if the tree was not changed since the last call to either getHTML or getScript. If the arguments are used, they mean the same as described under getHTML

Accessors

HTML::Widget::SideBar adds to the methods of its base class the following accessors:

getUniqueId

Returns the unique Id that the menu will recieve when built with this node as root.

getAction / setAction ($action)

Gets and sets the item's action. If no action is given to setAction, the default do-nothing action is used.

setToggleAction

Sets the item's action to the stock toggle action described above.

getURL / setURL ($url)

Gets and sets the item's URL.

getActive / setActive ($status)

Gets and sets the item's active status. if the argument for setActive is ommitted, 1 is assumed.

Class methods

The following class methods help you generate supporting code for your menus:

baseJS

Returns the basic Javascript code for use with this module's toggle feature.

deepBlueCSS

Returns a datastructure for buildCSS. Using the properties provided by this function will result in a sidebar in shades of blue. You can tweak this to your satisfaction.

buildCSS ($css)

Takes a data structure and returns a string with valid CSS you can incorporate into your document.

The data structure is as follows:

A main hash with one key for each element of the sidebar (bar, list, item etc). The value for each key is again a hash with CSS property - value pairs, like top => 1, left => 1 etc. If a key is preceded by an underscore, it is converted into the :hover definition for the class of that name (this should be a name given to one of the other classes).

METHOD SUMMARY (NEW + INHERITED)

The following is a categorized list of all available meyhods, for quick reference. Methods that do not appear in the source of this module are marked:

Object lifecycle:

new, readDB, *delete, *append.

Iterating and managing children:

*nextNode, *reset, *savePlace, *restorePlace, *childCount, *getSubTree, *follow

Generating code:

deepBlueCSS, buildCSS, baseJS, getHTML

Fields:

*addField, *removeField, setField, *setFields, *getField, *getFields, *hasField.

BROWSER COMPATIBILITY

Tested on IE6 and Firefox 1.0PR and worked. On Konqueror it's OK too but not thoroughly tested. If you test it on other browsers, please let me know what is the result.

EXAMPLES

testbar.pl in the examples directory shows how it's done. perl-begin.css in the same place is a full style sheet taken from a site that uses this module.

BUGS

Directly to the author, please.

SEE ALSO

Tree::Numbered, Tree::Numbered::Tools, Tree::Numbered::DB, Javascript::Menu

AUTHOR

Yosef Meller, <mellerf@netvision.net.il>

CREDITS

Shlomi Fish added the render_hidden attribute, and some of the code that separates the script from the HTML. Also supplied the CSS for examples/perl-begin.css

COPYRIGHT AND LICENSE

Copyright 2004 by Yosef Meller

This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.

Exception: the file examples/perl-begin.css is not by me and uses a different license. See the head of that file for details.




Hosting generously
sponsored by Bytemark