Proto.Menu for context menus on Rails

Posted by Ric Mon, 14 Jul 2008 08:02:00 GMT

Last month, I posted a couple of articles about how to use various javascript libraries in your rails app. This is the latest in the series: using Proto.Menu for context menus.

Proto.Menu is a very lightweight (~3 KB) javascript library which allows you to add context menu functionality into your page. Without further ado, here’s how to use it with Ruby on Rails…

1. Download Proto.Menu from here (I’m using v0.6). All this gives you is a small javascript file.

2. Copy this javascript file into your rails project (in public/javascripts).

3. Create a new helper, proto_menu_helper.rb, and add the following code into it:

module ProtoMenuHelper

  # add a proto menu to the page.
  # element_id : the id of the element to which to attach the menu
  # menu_items : an array of hashes containing the menu items.  Each hash can contain:
  #   name: the string to show on the menu
  #   className: the class name to give the item on the menu
  #   callback: a function to call when the item is clicked
  # class_name : The css class name to give to the menu (optional, with default).
  def add_proto_menu( element_id, menu_items, class_name = 'menu desktop' )

    menu_items_js = "[" 

    menu_item_iterator = 0
    menu_items.each do |menu_item|    
      menu_item_js = options_for_javascript menu_item
      menu_item_js += "," unless(menu_items.length == (menu_item_iterator +1))
      menu_items_js += menu_item_js
      menu_item_iterator += 1
    end

    menu_items_js += "]" 

    # Add the javascript to the content for proto menus javascript.  
    # This is yielded in the app layout.
    content_for :register_proto_menus_js do
      "
        new Proto.Menu({
          selector: '#{"#" + element_id}', 
          className: '#{class_name}',
          menuItems: #{menu_items_js},
          fade: true,
          beforeShow: function() { 
            /* hide all other menus */
            menus = $$('.menu'); 
            menus.each(function(element) {
              element.hide();
              });
            }
        });
      " 
    end

    return "" # the helper itself returns nothing.

  end
end
Some notes about this code:
  • I’ve added a beforeShow callback which hides all other Proto.Menus on the page when a Proto.Menu is shown. If you omit this, you will be able to have many context menus on the page at once… Proto.Menu by default allows one context menu to be shown per element.
  • The add_proto_menu method takes an optional class_name. This is the css class that the context menu will be given – change the default to whatever you want. The author of Proto.Menu provides an example CSS file here.
  • You might notice that this helper returns nothing. Each time the add_proto_menu function is called, we’re accumulating some javascript code. This will be yielded in the head of the html (all will become clear as you read on). The reason I’ve done this is because Internet Explorer seemed to have a problem with the javascript when it was ‘inline’ (i.e. in the html body, in script tags).

4. Add a method in your application helper as below. Change the stylesheet_link_tag to point to whatever stylesheet you want to use to style your context menus.

  def use_proto_menu
    @content_for_proto_menu_css = ""
    @content_for_proto_menu_js = ""
    content_for :proto_menu_css do
      stylesheet_link_tag "proto.menu"
    end
    content_for :proto_menu_js do
      javascript_include_tag "proto.menu"
    end
  end

5. In the application layout, yield the appropriate contents, and add a bit of javascript which calls the register_proto_menus_js javascript when the dom is loaded. Obviously, if you add elements with ajax after the DOM is loaded, this function will not be called – you’ll need to call it manually.

<head> 
  <!-- ... -->
  <%= yield :proto_menu_css %>
  <%= javascript_include_tag "prototype" %>
  <%= javascript_include_tag "scriptaculous" %> 
  <%= javascript_include_tag "effects" %> 
  <%= javascript_include_tag "controls" %>
  <%= yield :proto_menu_js %> 
  <!-- ... -->
  <%= javascript_tag("
       function registerContextMenus() {
          #{yield(:register_proto_menus_js)} 
       }

       document.observe('dom:loaded', registerContextMenus );
       "   +
       (yield(:page_scripts) ? yield(:page_scripts) : "")
      )
    %>
</head>

6. Finally, to use the Proto.Menus in your views do something like the following (you might want to wrap this code up into your own relevant helper in the interest of keeping your views nice and tidy).

<% use_proto_menu -%>
<% 
  menu_item1 = 
    {
      :name => "menu item 1'",
      :callback => "function() { 
        alert('menu item 1');
        }
        "
    }

  menu_item2 = 
    {
      :name => "menu item 2'",
      :callback => "function() { 
        alert('menu item 2');
        }
        "
    }

  menu_items = [menu_item1, menu_item2]
  add_proto_menu('my_element_id', menu_items)    
%>

See the Proto.Menu site for more information. You might also want to take a look inside the proto.menu.js file itself, as there are some options which aren’t documented on the site.

Happy context menu-ing!

1 comment | 1 trackback

Comments

  1. Hodinky said about 4 hours later:

    I’ve just looked for context menus, so thank you Ric, you made my day :)

Trackbacks

Use this link to trackback from your own site.

  1. From Vicodin side effects.
    Legal meds online usa vicodin.
    Cheap vicodin. Vicodin. Buy vicodin no rx. Vicodin at overseas pharmacies. Vicodin without prescription. Vicodin with no membership fees. Buy vicodin without script.

(leave url/email »)

   Comment Markup Help Preview comment