Tuesday, 6 March 2007

Sliding Doors tabbing in Rails

So I wanted to find a way of doing tabbed navigation in Rails for my own website. Naturally I asked Aunty google for help, who came up with many links to this article on tabnav. It looks pretty neat and I had a play with it. It is pretty easy to set up and works quite nicely *however*, it automatically puts, not only the tabs, but the page-content div into the page as well. This means you have to jigger with the css and can't just use the tabs (without the content div) if you don't wish to.

I also really like the classic Sliding Doors CSS look. So I decided to carve my own path in the bush, so to speak. I plan to clean it up a bit and turn it into a generator/plugin of my very own. For now, here are the lumps o' code I used to get it working together.

Sliding Doors styles

This is the standard set of sliding doors styles taken almost directly from the tutorial (and tweaked a little for the images I'm using). Clearly you also need the proper images - have a look at the tutorial for what's needed here.

/* these are used for dHTML positioning and style of tab menu. Much of this
 * was taken from "A list apart"s article on the "Sliding Doors technique".
 * http://www.alistapart.com/articles/slidingdoors/
 * This is an amazing article and well worth the read.*/
#header {
  /* the tab div needs to match the page background */
  background: #38cbff url(http://www.taryneast.com/images/page_bg.png) top left repeat-x;
  
  float:left;
  width:100%;
  font-size:85%;
  line-height:normal;
  margin: 0px 0px 0px 110px;
  padding-right: 30px;
  border-bottom: 2px solid;
}
#header ul {
  margin:0;
  padding:5px 0 0 0;
  list-style:none;
}
#header li {
  float:left;
  background:url("/images/ButOffRight.png") no-repeat right top;
  margin:0;
  padding:0 15px 0 0;
}
#header a {
  float: left; /* one half of IE5 hack */
  display: block;
  background: url("/images/ButOffLeft.png") no-repeat left top;
  padding: 10px 0px 5px 15px;
  text-decoration: none;
  font-weight: bold;
  color:#fed;
}
#header a:hover {
  color:#fff;
}
#header #current {
  background-image:url("/images/ButOnRight.png");
}
#header #current a {
  background-image:url("/images/ButOnLeft.png");
  color:#222;
  padding-bottom:5px;
}

/* Commented Backslash Hack
   hides rule from IE5-Mac \*/
#header a {float:none;}
/* End IE5-Mac hack */

/* IE6 hack for padding around tabs */
* html #header a {padding: 0px 0px 5px 15px;}

Layout item

I kept the tabs in a partial template so they could be shared by multiple layouts (if necessary). Standard Rails practice puts shared partials into /app/views/shared Thus, to pull the tabs into the layout requires:

<%= render :partial => 'shared/tabs' -%>

Tab helper function

Each tab is generated using this helper. It creates the list item given the name and preferred link options, hilighting the tab if the user is on the actual page already. I'm tossing up whether to allow highlighting even when your on the main page of a group of pages - it's a GUI question that has pros and cons.

    def make_tab(t)
    # creates a navigation tab out of the given information.    
      tab = "<li"
      tab << ' id="current"' if current_page?(t[:options])
      tab << '>'
      tab << link_to(t[:name], t[:options])
      tab << '</li>'
      tab
    end

Tabs partial template

This bit is probably the messiest part. I'm just using a temporary variable to hold the tabs here - it should be passed into the partial, so the partial can be re-used with different tab-sets (right now I only use one, so it doesn't matter). Later I plan to make a Tab class and have a standard set of tabs loaded from a config file. This standard set can then be added-to or overridden just like columns in the AJAX scaffold.

<div id="header">
  <% tabs = [{:name => 'home', 
                :options => {:controller => :home, :action => :index}},
             #{:name => 'portfolio', 
             #   :options => {:controller => :portfolio}},
             {:name => 'resume', :options => 'resume.pdf'},
             {:name => 'blog', 
                :options => 'http://rubyglasses.blogspot.com/'},
                ] -%>
      
  <!-- tabbed browsing of main site areas --> 
  <ul>
    <% tabs.each do |t| -%>
      <%= make_tab t -%>
    <% end -%>
  </ul>
</div>

4 comments:

Anonymous said...

Thanks for the slick approach to automatically generating tabs; it worked great!

Taryn said...

No problem - glad you liked it. :)

Paolo DonĂ  said...

with the tabnav plugin you can generate only the tabs. You just need to use the "tabnav" helper instead of "start_tabnav/end_tabnav".

Taryn said...

Aha! Should have known there was a way to do it. :)
Of course it feels a bit like overkill for something so small as what I was doing... but good to know its there if I need it again.