Skip to content

Instantly share code, notes, and snippets.

@donarus
Last active December 28, 2015 19:59
Show Gist options
  • Save donarus/7554148 to your computer and use it in GitHub Desktop.
Save donarus/7554148 to your computer and use it in GitHub Desktop.
Simple reimplementation of CSS3 Tree (by Martin Ivanov) as a component for Tapestry5-jquery framework (standard one is too complex and does not allow using of zones) http://acidmartin.wordpress.com/2011/09/26/css3-treevew-no-javascript/
/*
* CSS3 Treeview by Martin Ivanov
* http://wemakesites.net
*/
.css-treeview ul,
.css-treeview li
{
padding: 0;
margin: 0;
list-style: none;
}
.css-treeview input
{
position: absolute;
opacity: 0;
}
.css-treeview
{
font: normal 11px "Segoe UI", Arial, Sans-serif;
-moz-user-select: none;
-webkit-user-select: none;
user-select: none;
}
.css-treeview a
{
color: #00f;
text-decoration: none;
}
.css-treeview a:hover
{
text-decoration: underline;
}
.css-treeview input + label + ul
{
margin: 0 0 0 22px;
}
.css-treeview input ~ ul
{
display: none;
}
.css-treeview label,
.css-treeview label::before
{
cursor: pointer;
}
.css-treeview input:disabled + label
{
cursor: default;
opacity: .6;
}
.css-treeview input:checked:not(:disabled) ~ ul
{
display: block;
}
.css-treeview label,
.css-treeview label::before
{
background: url("simpletree-icons.png") no-repeat;
}
.css-treeview label,
.css-treeview a,
.css-treeview label::before
{
display: inline-block;
height: 16px;
line-height: 16px;
vertical-align: middle;
}
.css-treeview label
{
background-position: 18px 0;
}
.css-treeview span {
cursor: pointer;
}
.css-treeview-marked
{
font-weight:bold;
}
.css-treeview label::before
{
content: "";
width: 16px;
margin: 0 22px 0 0;
vertical-align: middle;
background-position: 0 -32px;
}
.css-treeview input:checked + label::before
{
background-position: 0 -16px;
}
/* webkit adjacent element selector bugfix */
@media screen and (-webkit-min-device-pixel-ratio:0)
{
.css-treeview
{
-webkit-animation: webkit-adjacent-element-selector-bugfix infinite 1s;
}
@-webkit-keyframes webkit-adjacent-element-selector-bugfix
{
from
{
padding: 0;
}
to
{
padding: 0;
}
}
}
package cz.donarus.tagr.web.components.bootstrap;
import org.apache.tapestry5.BindingConstants;
import org.apache.tapestry5.ClientElement;
import org.apache.tapestry5.ComponentResources;
import org.apache.tapestry5.Link;
import org.apache.tapestry5.annotations.*;
import org.apache.tapestry5.internal.util.CaptureResultCallback;
import org.apache.tapestry5.ioc.annotations.Inject;
import org.apache.tapestry5.json.JSONArray;
import org.apache.tapestry5.json.JSONObject;
import org.apache.tapestry5.services.javascript.JavaScriptSupport;
import org.apache.tapestry5.tree.TreeModel;
import org.apache.tapestry5.tree.TreeNode;
import java.util.List;
/**
* @author: donarus
*/
@Import(stylesheet = {"simpletree.css"}, library = {"simpletree.js"})
@Events({SimpleTree.NODE_SELECTED, SimpleTree.NODE_UNSELECTED})
public class SimpleTree implements ClientElement {
public static final String NODE_SELECTED = "nodeSelected";
public static final String NODE_UNSELECTED = "nodeUnselected";
@Parameter(required = true, autoconnect = true)
private TreeModel model;
@Inject
private ComponentResources componentResources;
@Inject
private JavaScriptSupport support;
@Property
private TreeNode node;
@Property
private String id;
@Parameter(defaultPrefix = BindingConstants.LITERAL)
private String zone;
private Link createExplodeLink() {
return componentResources.createEventLink("explode");
}
private Link createSelectedLink() {
return componentResources.createEventLink("selected");
}
public JSONObject onExplode(@RequestParameter("t:nodeId") String nodeId) {
JSONObject response = new JSONObject();
response.append("parentId", nodeId);
TreeNode parent = model.getById(nodeId);
JSONArray children = new JSONArray();
List<TreeNode> childrenNodes = parent.getChildren();
for (TreeNode node : childrenNodes) {
JSONObject child = new JSONObject();
child.put("label", node.getLabel());
child.put("id", node.getId());
child.put("hasChildren", node.getHasChildren());
children.put(child);
}
response.put("children", children);
return response;
}
Object onSelected(@RequestParameter("t:nodeId") String nodeId, @RequestParameter("t:selected") boolean selected) {
TreeNode node = model.getById(nodeId);
String event = selected ? NODE_SELECTED : NODE_UNSELECTED;
CaptureResultCallback<Object> callback = CaptureResultCallback.create();
componentResources.triggerEvent(event, new Object[]{nodeId}, callback);
final Object result = callback.getResult();
return (result != null) ? result : new JSONObject();
}
public List<TreeNode> getRootNodes() {
return model.getRootNodes();
}
@AfterRender
void afterRender() {
JSONObject params = new JSONObject();
params.append("explodeUrl", createExplodeLink().toString());
params.append("selectedUrl", createSelectedLink().toString());
params.append("clientId", getClientId());
if (zone != null) {
params.put("zone", zone);
}
support.addInitializerCall("simpleTree", params);
}
private String clientId;
public String getClientId() {
if (clientId == null) {
if (id != null) {
clientId = support.allocateClientId(id);
} else {
clientId = support.allocateClientId(componentResources);
}
}
return this.clientId;
}
}
function addRequestParameter(name, value, url) {
if (url.indexOf('?') < 0) {
url += '?'
} else {
url += '&';
}
value = escape(value);
url += name + '=' + value;
return url;
}
(function($) {
$.fn.simpleTree = function(spec) {
var cfg = spec;
var tree = $('#' + cfg.clientId);
function expand(input) {
if (!input.data('loaded')) {
var nodeId = input.closest('li').data('id');
var explodeUrl = cfg.explodeUrl;
explodeUrl = addRequestParameter('t:nodeId', nodeId, explodeUrl);
input.data('loaded', true);
$.ajax({
dataType: "json",
url: explodeUrl,
success: processExpand
});
}
}
function mark(item) {
var nodeId = item.closest('li').data('id');
var marked = item.hasClass('css-treeview-marked');
if (cfg.zone) {
var selectedUrl = cfg.selectedUrl;
selectedUrl = addRequestParameter("t:selected", !marked, selectedUrl);
selectedUrl = addRequestParameter("t:nodeId", nodeId, selectedUrl);
var specs = { url : selectedUrl };
$('#' + cfg.zone).tapestryZone("update", specs);
} else {
$.ajax({url:cfg.selectedUrl, data: { "t:nodeId": nodeId, "t:selected": !marked }});
}
if (marked) {
item.removeClass('css-treeview-marked');
} else {
item.addClass('css-treeview-marked');
}
}
function processExpand(json) {
var parent = tree.find('li[data-id='+json.parentId+']');
var childrenList = $('<ul>');
parent.append(childrenList);
var children = json.children;
$.each(children, function(i, item) {
var childListItem = $('<li>', {
'data-id': children[i].id,
});
if (children[i].hasChildren) {
var input = $('<input type="checkbox">');
input.click(function() {
expand(input);
});
childListItem.append(input);
var label = $('<label><span data-selectable="true">' + children[i].label + '</span></label>');
childListItem.append(label);
label.children('span[data-selectable="true"]').click(function() {
mark($(this));
});
} else {
var span = $('<span data-selectable="true">' + children[i].label + '</span>');
span.click(function() {
mark($(this));
});
childListItem.append(span);
}
childrenList.append(childListItem);
});
}
var inputs = tree.find('input');
inputs.removeAttr('checked');
inputs.click(function() {
expand($(this));
});
var selectables = tree.find('span[data-selectable="true"]');
selectables.click(function() {
mark($(this));
});
};
T5.extendInitializers(function() {
function init(spec) {
$('#' + spec.clientId).simpleTree(spec);
}
return {
simpleTree : init
}
});
})(jQuery);
<t:container xmlns:t="http://tapestry.apache.org/schema/tapestry_5_3.xsd" xmlns:p="tapestry:parameter">
<div class="css-treeview" id="${clientId}">
<ul>
<li t:type="loop" t:source="rootNodes" t:value="node" data-id="${node.id}">
<t:if t:test="node.hasChildren">
<input type="checkbox"/><label><span data-selectable="true">${node.label}</span></label>
<p:else>
<span data-selectable="true">${node.label}</span>
</p:else>
</t:if>
</li>
</ul>
</div>
</t:container>
...
public TreeModel<Place> getPlaceModel() {
return placeModel;
}
@OnEvent(value = SimpleTree.NODE_SELECTED)
public Zone nodeSelected(long nodeId) {
...
return myZone;
}
@OnEvent(value = SimpleTree.NODE_UNSELECTED)
public Zone nodeUnSelected(String placeId) {
...
return myZone;
}
...
...
<t:bootstrap.simpleTree t:zone="myZone" t:model="myModel"/>
...
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment