Wednesday, 1 May 2013

CSS pull down menu using APEX List

I find APEX tab sets cause all sorts of issues in applications, either through management or behaviour. A common request is to create some pull-down menus as a replacement.
There are plenty of options for this, including a number of jQuery plugins, but here is an example that uses only CSS
http://www.lwis.net/free-css-drop-down-menu/ultimate.horizontal.html
This makes it lightweight and fast.

It's easy to implement this as a list template in APEX, enabling you to source your menu from a SQL query (possibly on your page meta-data) or a static list.

You might have seen this in the past as a Sumneva presentation from Scott Spendolini, which somehow I found after encountering the LWIS site - but it did confirm a few gaps for me. It also provides a good review of Tabs vs jQuery vs CSS.

I personally had a few issues implementing this as described, but I think there are some minor bugs during the rendering if list templates.
The following example renders it fine.

Upload supporting files

Upload the following files to your workspace - downloadable here under MIT/GNU licence.
default.css
default.ultimate.css
dropdown.css

Or place them in your fileserver - whichever is appropriate.
You'll also need these images (in this example, anyway)
grad1.png
grad2.png
nav-arrow-down.png
Note no JavaScript files required.

Create list template

Create new list template from scratch (don't be scared!)

Name: CSS Horizontal Dropdown Menu
Class: Pull Down Menu or Custom n -- either would be worthwhile

Once created, edit the template and enter the following in the relevant locations, or download the template export from here and adjust the supporting file locations accordingly.

Before List Entry
"List template before rows"
Here you'll need to modify the CSS location as appropriate. In fact when loading things into the APEX workspace, default.css also needs to be included here and the @import command removed from default.ultimate.css
Likewise, the CSS needs to be modified so all the supporting images are referenced using something like
background-image: url(http://www.lwis.net/free-css-drop-down-menu/images/default/nav-arrow-down.png);
unless you come to other arrangements.
<link href="#WORKSPACE_IMAGES#default.css" media="screen" rel="stylesheet" type="text/css" />
<link href="#WORKSPACE_IMAGES#dropdown.css" media="screen" rel="stylesheet" type="text/css" />
<link href="#WORKSPACE_IMAGES#default.ultimate.css" media="screen" rel="stylesheet" type="text/css" />
<div id="navwrap">
<ul id="nav" class="dropdown dropdown-horizontal" style="margin-bottom:10px;">


<style> 
ul {
  list-style-type: none;
}

#navwrap {
  float:left;
  width:100%;
  margin-bottom:4px;
  background:#f6f6f6;
  border-style:solid;
  border-color:#d9d9d9 #d9d9d9 #d9d9d9;
  border-width:1px 1px 1px 0;
}

</style>

This also includes an override for the user agent stylesheet for unordered lists, otherwise you see bullet points instead of images.
The navwrap ID settings pushes the menu across the width of the page.

Template Definition
"List template current"
<li><a href="#LINK#">#TEXT#</a></li>

"List template current with sublist items"
<li><a style="color:black;" class="dir">#TEXT#</a><ul>

"List template non current"
<li><a href="#LINK#">#TEXT#</a></li>

"List template noncurrent with sublist items"
<li><a style="color:black;" class="dir">#TEXT#</a><ul>

Before Sublist Entry
"Sublist template before rows"
<!-- I don't believe this renders with dynamic lists!
this is why the opening UL is in the definitions above -->


Sublist Entry
"Sublist template current"
<li><a href="#LINK#">#TEXT#</a></li>

"Sublist template current with sublist items"
<li><a href="#LINK#" class="dir">#TEXT#</a>

"Sublist template noncurrent"
<li><a href="#LINK#">#TEXT#</a></li>

"Sublist template noncurrent with sublist items"
<li><a href="#LINK#" class="dir">#TEXT#</a>

After Sublist Entry
"Sublist template after rows"
</ul></li>

After List Entry
"List template after rows"
 </li></ul></div>

Prepare menu hierarchy

Before defining the region that renders the list, you need a list defined - this will be your hierarchical menu. This way it can either be a static list where you manually define the menu options, or it could be a query over your application's meta-data.

In this example I'll use a dynamic query - but before creating your list, we need to prepare the hierarchy.

I defined a few Page Groups to be my menu headings, and the allocated pages will live under the relevant drop down.
Application Builder -> Utilities -> Cross page utilities -> Page groups
Page group allocation

Create List 

Now we've prepared the meta-data, we can create a list based on a query that our list region will use.
The query is structure in three parts -
  • Home link - often identified by page alias of HOME, but you could get clever with this. 
Once I embedded this into the template itself, allowing me to parameterise a logo as the home link via substitution strings
<li >
  <a href="f?p=&APP_ID.:HOME:&SESSION." style="padding:0 5px;">
    <img src="&TEMPLATE_LOCATION.logo_&APP_ALIAS..png" style="max-height:26px;"/>
</a></li>   
  • Top menus - by default this are links, but I've modified these to just facilitate opening the menu - particularly for mobile devices where your finger is used instead of a mouse hover.
  • Drop down options - the destination pages for your menu
So create a new dynamic list from scratch called Menu. Use the following query
select -- Link to home page
   1        lvl
  ,page_name   label
  ,'f?p='||:APP_ID||':'||page_id||':'||:APP_SESSION||'::'||:DEBUG  target
  ,null  is_current_list_entry
  ,null  image
  ,null  image_attribute
  ,null  image_alt_attribute
  ,page_alias order1
from apex_application_pages ap
where application_id = :APP_ID
and page_alias = 'HOME' -- identify however you like
union all
select -- Menu options, don't link anywhere to aid touch devices
   1        lvl
  ,page_group_name label
  ,'#' target
  ,null  is_current_list_entry
  ,null  image
  ,null  image_attribute
  ,null  image_alt_attribute
  ,page_group_name order1
from apex_application_page_groups pg
where application_id = :APP_ID
union all
select -- drop down menu options - pages belonging to groups
   2        lvl
  ,page_title   label
  ,'f?p='||:APP_ID||':'||page_id||':'||:APP_SESSION||'::'||:DEBUG  target
  ,null  is_current_list_entry
  ,null  image
  ,null  image_attribute
  ,null  image_alt_attribute
  ,page_group order1
from apex_application_pages ap
where application_id = :APP_ID
and page_group is not null -- you may limit this to set groups, allowing further management of utility pages, popups etc
order by  order1, lvl,label
If you find the menu not displaying in the hierarchy you expected, check the order by. It should group results as you might translate to the menu - all the relevant menus together, and the top menu is the first record of each group.

Create list region

Create your global page (page zero) if you haven't done so already. This is the perfect place for your menu's list region.
Create a new list region in the global page, with the display point appropriate for your particular template. I chose region position 1. The region requires no template, but you use the List Template you just created.
You will also need to ensure the menu doesn't appear on pages that don't require it - logon, popups, etc. So make sure your region condition is appropriate. To start with it might be
:APP_PAGE_ID != 101

Remove tabs

Now that you have a CSS menu system in place, you can now remove the tab list that may have previously supported your pages. You might be able to make one change, depending on how you've managed your page templates. This could be modifying the Page template default in your current theme from "One Level Tabs" to "No Tabs". For the theme (2) I have been using in my sample application I also needed to remove the div that housed the tab set. 
CSS menu system
It's not quite as pretty as when implemented using one of the newer themes, but still looks neat and clean. I'll have to update this in future. You may also wish to replace the pixel style down arrows to something more contemporary, less retro.

This biggest bugbear I have with this solution is it doesn't seem touch-screen friendly, despite my interpretation of assurances in the LWIS FAQ. While it drop's down, it doesn't allow me to select "Employees" using Chrome on my Samsung Galaxy III. On a Windows Latitude using IE, the drop-downs don't even open. First level selections are ok.

A similar alternative is an Enkitec plug-in incorporating the Twitter Bootstrap framework.

Scott

ps - a few weeks after I drafted this I noticed Dutch blogger Kees Vlek posting about the same framework.
Post a Comment