Monday, December 2, 2013

Pure CSS3 Org-Tree with APEX List

employees-tree
I found this little CSS3 snippet gem on TheCodePlayer.com: nested HTML unordered lists styled as a hierarchical tree structure just by applying some (very nifty) CSS selectors. Naturally I had to try this in APEX. With some minor modifications in the CSS and creating a simple List Template, the code works great for visualizing hierarchical data, like for the Employee/Manager relationship used in the demo.
Here the step-by-step tutorial to implement this solution in Application Express:

Nested List Template

If you have a look at the original code snippet, you will see a DIV container enclosing a simple nested list:


<div class="tree">
    <ul>
        <li>
            <a href="#">Parent</a>
            <ul>
                <li>
                    <a href="#">Child</a>
                    <ul>
                        <li>
                            <a href="#">Grand Child</a>
                        </li>
                    </ul>
                </li>
                <li>
                …
                …
                </li>
            </ul>
        </li>
    </ul>
</div>
Nested lists can easily be implemented with a List Template. I created a new template (name: Org Tree) from scratch with the following definition:

Before List Entry:

<div class="tree">
<ul>

Template Definition

List Template Current <li><a href="#LINK#">#TEXT#</a></li>
List Template Noncurrent <li><a href="#LINK#">#TEXT#</a></li>

Before Sublist Entry

<ul>

Sublist Entry

Sublist Template Current
<li><a href="#LINK#">#TEXT#</a></li>
Sublist Template Noncurrent
<li><a href="#LINK#">#TEXT#</a></li>

After Sublist Entry

</ul>

After List Entry

</ul>
</div>

The List

That’s it. Now define a hierarchical SQL query (I took the EMP example from the “Create List Wizard”) and create a List Shared Component:
list-tree01

The Page

The page is where the list and the List Template come together in a region. Use the “Create Region” wizard to add a region to your page using the list en template we just defined:
list-tree03

The CSS

I copied the original CSS snippet from TheCodePlayer.com and placed this into the Inline CSS property of my page. I had to make a minor modification, because when resizing the page (I’m using Theme 25 here) the tree structure would break. So here is the final CSS code as used in my demo page:
.tree {
  overflow-x: auto;
}
.tree ul {
  padding-top: 20px;
  position: relative;
  white-space: nowrap;
}
.tree li {
  display: inline-block;
  white-space: nowrap;
  vertical-align: top;
  margin: 0 -2px;
  text-align: center;
  list-style-type: none;
  position: relative;
  padding: 20px 5px 0;
  transition: all .5s;
  -webkit-transition: all .5s;
  -moz-transition: all .5s;
}
/*We will use ::before and ::after to draw the connectors*/
.tree li::before,.tree li::after {
  content: '';
  position: absolute;
  top: 0;
  right: 50%;
  border-top: 1px solid #ccc;
  width: 50%;
  height: 20px;
}
.tree li::after {
  right: auto;
  left: 50%;
  border-left: 1px solid #ccc;
}
/*We need to remove left-right connectors from elements without any siblings*/
.tree li:only-child::after,.tree li:only-child::before {
  display: none;
}
/*Remove space from the top of single children*/
.tree li:only-child {
  padding-top: 0;
}
/*Remove left connector from first child and 
right connector from last child*/
.tree li:first-child::before,.tree li:last-child::after {
  border: 0 none;
}
/*Adding back the vertical connector to the last nodes*/
.tree li:last-child::before {
  border-right: 1px solid #ccc;
  border-radius: 0 5px 0 0;
}
.tree li:first-child::after {
  border-radius: 5px 0 0 0;
}
/*Time to add downward connectors from parents*/
.tree ul ul::before {
  content: '';
  position: absolute;
  top: 0;
  left: 50%;
  border-left: 1px solid #ccc;
  width: 0;
  height: 20px;
}
.tree li a {
  border: 1px solid #ccc;
  padding: 5px 10px;
  text-decoration: none;
  color: #666;
  font-family: arial, verdana, tahoma;
  font-size: 11px;
  display: inline-block;
  -webkit-border-radius: 5px;
  -moz-border-radius: 5px;
  border-radius: 5px;
}
/*Time for some hover effects*/
/*We will apply the hover effect the the lineage of the element also*/
.tree li a:hover,.tree li a:hover+ul li a {
  background: #c8e4f8;
  color: #000;
  border: 1px solid #94a0b4;
}
/*Connector styles on hover*/
.tree li a:hover+ul li::after,.tree li a:hover+ul li::before,.tree li a:hover+ul::before {
  border-color: #94a0b4;
}


themes4apex

3 comments:

  1. Good job Christian. I will try it out in my family tree app and see how it will handle large trees. :)

    ReplyDelete
  2. Hi Niels,

    Thanks for testing. For (very) large/wide trees, you need to add an additional CSS attribute to the UL:

    white-space: nowrap;

    Like this, a scrollbar will be displayed if the tree exceeds the regions width and the tree won't break.

    Regards,
    Christian

    ReplyDelete