more cracked-out scaffolding: Element.twisty

Courtenay : February 2nd, 2006

For anyone following this, I'm slowly building you up to a fully-featured sortable, filtered scaffold helper library. You may recall we already built filtering with SELECT dropdowns. I'm going to add an input box and some sorting to that. First, though, we need a little sumthin sumthin to simplify the UI: the twisty! The twisty is the proper name for that little triangly ► which turns into a ▼ when you click it, revealing some more information (maybe a tree node, or more options). We're going to extend prototype's Element object to create Element.twisty. It'll all make sense later, I promise. First, here's the functionality we're heading for:
more info

explanation

The code simply toggles a hidden div, and depending on the visibility, changes the little arrow to match. Things to look out for in this code: returns false, so you can return Element.twisty and it won't jump to the top of the page; takes a string or a DOM node; uses unicode for the twisties; requires a SPAN element in your A.





more info

you know I can't leave it alone

Why stop there? Let's set up a default box for the twisty to show/hide.. and while we're at it, if the twisty <a> doesn't have a SPAN in it, we'll generate one automagically.

Object.extend(Element, {
  twisty: function(element, _next) {
    element = $(element);
    var span = element.getElementsByTagName('SPAN');

    // automatically generate that SPAN if she doesn't exist
    if (span.length == 0) {
      span = document.createElement('span');
      span.className = 'twisty';
      element.appendChild(span);
    }
    else span = span[0];

    // use a default action to find the node to hide/show
    if (!_next) _next = findParent(element, 'li').nextSibling;
    span.innerHTML = (_next.style.display == 'none') ? '▼' : '►'
    Element.toggle(_next);
    return false;
  }
});

  • Toggle
  • This is your content box
This way, it will apply automatically to a UL by hiding or showing the next LI, unless you override _next. so <a onclick='return Element.twisty(this)' href='#' > will work. Of course you can modify this yourself. (FYI, this example won't quite work as published, you need to Element.cleanWhitespace the parent UL in firefox, but I'm trying to keep it simple)

OK, so we have a twisty

Let's add sorting to our filterable table header. To keep the UI nice and simple, we're going to hide the filter boxes and make the TH clickable for sorting.

monkey foo Non-sortable filter
monkey one something sample data
hint: click 'filter'
monkey foo Non-sortable filter
monkey one something sample data
monkey two somethin else jaket sux

You're probably going to want some signifier of order (ascending, descending) so we'll use the unicode characters ▲ and ▼ I'm going to make this one a helper method, so our code looks nicer.


  def table_sorting_header(fieldname, options = { :default => false })
    params[:order] ||= "#{fieldname} asc" if options[:default] == true
    if params[:order] == "#{fieldname} asc"
      "#{fieldname}" +
      ""
    elsif params[:order] == "#{fieldname} desc"
      "#{fieldname}" +
      ""
    else
      "#{fieldname}"
    end
  end

Call it like <%= table_sorting_header 'monkey' %> or if you sort one of the columns by default, <%= table_sorting_header 'id', { :default => true } %> on that column.

There's the view done.. hook it up!

Open up the controller and fire away.. You'll need the revised Hash#to_sql that converts '%' queries to 'LIKE'.

def list
    @order = params[:order].gsub(/^(\w)\s/, "LOWER ($1) ") if params[:order] =~ /^(monkey|foo|id) (asc|desc)$/
    @conditions[:monkey] = "%#{params[:monkey]}%" } if params[:monkey]
    @conditions[:foo] = "%#{params[:foo]}%" if params[:foo]
    @monkey_pages, @monkeys = paginate :monkey, :per_page => 20, :order => @order, :conditions => @conditions.to_sql
end
As always, fire away with your comments and questions..

3 Responses to “more cracked-out scaffolding: Element.twisty”

  1. Scott Says:
    This is rad. I like where this is going. I've already dabbled in setting up something like this: Ruby on Rails Grid Control RoR Grid Control Part II I am definitely going to incorporate your ideas to improve it... Now if we could just make it look like this (clean refined look, scrollable data, fixed headers, resizable columns) And work like this (no paging, just scrolling, with ajax) One can dream. :)
  2. Bob Aman Says:
    Aww, sweet. But why put the arrow _after_ the text? It looks so much nicer before it...
  3. kamal Says:
    I believe in Mac UI parlance, this is called the Disclosure Triangle.

Sorry, comments are closed for this article.