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

8 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
  3. Hi Christian,

    How to get the Editor settings lke HTML/XML , PL/SQL IDE like structure for Text Area ? THe way our screenshots show,
    How can we get that ?
    it is a really good way to code fast .

    Thanks

    ReplyDelete
    Replies
    1. Hi,

      I'm using the APEX Developer Addon build and provided free by Peter Raganitsch. You can download/request a copy on this site:

      https://apex.oracle.com/pls/apex/f?p=APEX_DEVELOPER_ADDON:ABOUT:0:::::

      Regards,
      Christian

      Delete
  4. hye, can I know how to display image of each employee in the chart? I really hope you can reply

    ReplyDelete
    Replies
    1. Hi,

      You just need to modify the List Template and facilitate the #IMAGE# attributes in your template definition. Next use the image columns in your list's query (see the query help).

      I haven't tested this with images instead of text yet. If the formatting breaks, you might need to adjust a bit of the CSS.

      If you are able to show, I would like to see you result.

      Regards,
      Christian

      Delete
    2. Thanks for the reply. Somehow it can display the image but with all employee with the same image. Is there anyway to pass the id from the query? Yeah sure, after I finish the chart, I will show to you.

      Delete