Rewrite Tree: basic node expansion
Project: http://git-wip-us.apache.org/repos/asf/tapestry-5/repo Commit: http://git-wip-us.apache.org/repos/asf/tapestry-5/commit/23a69ca7 Tree: http://git-wip-us.apache.org/repos/asf/tapestry-5/tree/23a69ca7 Diff: http://git-wip-us.apache.org/repos/asf/tapestry-5/diff/23a69ca7 Branch: refs/heads/5.4-js-rewrite Commit: 23a69ca7b052669dd4f3c3fcb1fba14079679ad6 Parents: fa70aae Author: Howard M. Lewis Ship <[email protected]> Authored: Sun Nov 18 00:09:58 2012 +0000 Committer: Howard M. Lewis Ship <[email protected]> Committed: Sun Nov 18 00:09:58 2012 +0000 ---------------------------------------------------------------------- .../coffeescript/META-INF/modules/core/tree.coffee | 72 +++++++++ .../apache/tapestry5/corelib/components/Tree.java | 118 +++++++++------ .../apache/tapestry5/corelib/components/Tree.tml | 16 +- 3 files changed, 150 insertions(+), 56 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/23a69ca7/tapestry-core/src/main/coffeescript/META-INF/modules/core/tree.coffee ---------------------------------------------------------------------- diff --git a/tapestry-core/src/main/coffeescript/META-INF/modules/core/tree.coffee b/tapestry-core/src/main/coffeescript/META-INF/modules/core/tree.coffee new file mode 100644 index 0000000..9242165 --- /dev/null +++ b/tapestry-core/src/main/coffeescript/META-INF/modules/core/tree.coffee @@ -0,0 +1,72 @@ +# Copyright 2012 The Apache Software Foundation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http:#www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# ## core/tree +# +# Handlers to support to the core/Tree Tapestry component +define ["core/dom", "core/ajax", "core/zone"], + (dom, ajax) -> + + TREE = "[data-component-type=core/Tree]" + NODE_ID = "data-node-id" + SELECTOR = "#{TREE} [#{NODE_ID}]" + + LOADING = "tree-children-loading" + LOADED = "tree-children-loaded" + + loadChildren = (node) -> + + # Ignore duplicate requests to load the children. + + return if node.meta LOADING + + container = node.findContainer TREE + url = container.attribute "data-tree-action-url" + + node.meta LOADING, true + + node.addClass "t-empty-node" + node.update "<span class='t-ajax-wait'/>" + + ajax url, + parameters: + "t:action": "expand" + "t:nodeid": node.attribute NODE_ID + onsuccess: (reply) -> + node.update("").removeClass "t-empty-node" + + label = node.findContainer("li").findFirst(".t-tree-label") + + label.insertAfter reply.responseJSON.content + + node.meta LOADING, false + node.meta LOADED, true + + clickHandler = -> + + # First case is dynamically loaded due to user action; second case + # is rendered with overall page due to server-side expansion model. + loaded = (this.meta LOADED) or (this.attribute "data-node-expanded") + + if (not loaded) and (not this.hasClass "t-empty-node") + loadChildren this + return false + + return false + + dom.onDocument "click", SELECTOR, clickHandler + + + return null + http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/23a69ca7/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Tree.java ---------------------------------------------------------------------- diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Tree.java b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Tree.java index fd639f4..4fd8ee8 100644 --- a/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Tree.java +++ b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Tree.java @@ -1,4 +1,4 @@ -// Copyright 2011 The Apache Software Foundation +// Copyright 2011, 2012 The Apache Software Foundation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -38,15 +38,22 @@ import java.util.List; * <p/> * The Tree component uses special tricks to support recursive rendering of the Tree as necessary. * - * @since 5.3 * @tapestrydoc + * @since 5.3 */ @SuppressWarnings( {"rawtypes", "unchecked", "unused"}) @Events({EventConstants.NODE_SELECTED, EventConstants.NODE_UNSELECTED}) +@Import(module = "core/tree") public class Tree { /** + * Name of query parameter that stores the node id of the node being operated on + * (expanded, collapsed, etc.). + */ + private static final String NODE_ID = "t:nodeid"; + + /** * The model that drives the tree, determining top level nodes and making revealing the overall structure of the * tree. */ @@ -134,8 +141,10 @@ public class Tree * This is a mix of immediate rendering, and queuing up various Blocks and Render commands * to do the rest. May recursively render child nodes of the active node. * - * @param node to render - * @param isLast if true, add "t-last" attribute to the LI element + * @param node + * to render + * @param isLast + * if true, add "t-last" attribute to the LI element * @return command to render the node */ private RenderCommand toRenderCommand(final TreeNode node, final boolean isLast) @@ -153,58 +162,31 @@ public class Tree writer.element("li"); if (isLast) + { writer.attributes("class", "t-last"); + } Element e = writer.element("span", "class", "t-tree-icon"); if (node.isLeaf()) + { e.addClassName("t-leaf-node"); - else if (!node.getHasChildren()) + } else if (!node.getHasChildren()) + { e.addClassName("t-empty-node"); + } boolean hasChildren = !node.isLeaf() && node.getHasChildren(); boolean expanded = hasChildren && expansionModel.isExpanded(node); - String clientId = jss.allocateClientId(resources); - - JSONObject spec = new JSONObject("clientId", clientId); - - e.attribute("id", clientId); + writer.attributes("data-node-id", node.getId()); - spec.put("leaf", node.isLeaf()); - - if (hasChildren) - { - Link expandChildren = resources.createEventLink("expandChildren", node.getId()); - Link markExpanded = resources.createEventLink("markExpanded", node.getId()); - Link markCollapsed = resources.createEventLink("markCollapsed", node.getId()); - - spec.put("expandChildrenURL", expandChildren.toString()) - .put("markExpandedURL", markExpanded.toString()) - .put("markCollapsedURL", markCollapsed.toString()); - - if (expanded) - spec.put("expanded", true); - } else + if (expanded) { - if (selectionModel != null) - { - // May need to address this in the future; in other tree implementations I've constructed, - // folders are selectable, and selections even propagate up and down the tree. - - Link selectLeaf = resources.createEventLink("select", node.getId()); - - spec.put("selectURL", selectLeaf.toString()); - if (selectionModel.isSelected(node)) - { - spec.put("selected", true); - } - } + writer.attributes("data-node-expanded", true); } - jss.addInitializerCall("treeNode", spec); - - writer.end(); // span.tx-tree-icon + writer.end(); // span.t-tree-icon // From here on in, we're pushing things onto the queue. Remember that // execution order is reversed from order commands are pushed. @@ -212,7 +194,6 @@ public class Tree queue.push(RENDER_CLOSE_TAG); // li if (expanded) - { queue.push(new RenderNodes(node.getChildren())); } @@ -222,9 +203,7 @@ public class Tree queue.push(RENDER_LABEL_SPAN); } - } - - ; + }; } /** @@ -264,7 +243,43 @@ public class Tree return className == null ? "t-tree-container" : "t-tree-container " + className; } - Object onExpandChildren(String nodeId) + public Link getTreeActionLink() + { + return resources.createEventLink("treeAction"); + } + + Object onTreeAction(@RequestParameter(NODE_ID) String nodeId, + @RequestParameter("t:action") String action) + { + if (action.equalsIgnoreCase("expand")) + { + return doExpandChildren(nodeId); + } + + if (action.equalsIgnoreCase("markExpanded")) + { + return doMarkExpanded(nodeId); + } + + if (action.equalsIgnoreCase("markCollapsed")) + { + return doMarkCollapsed(nodeId); + } + + if (action.equalsIgnoreCase("selected")) + { + return doUpdateSelected(nodeId, true); + } + + if (action.equalsIgnoreCase("deselect")) + { + return doUpdateSelected(nodeId, false); + } + + throw new IllegalArgumentException(String.format("Unexpected action: '%s' for Tree component.", action)); + } + + Object doExpandChildren(String nodeId) { TreeNode container = model.getById(nodeId); @@ -273,21 +288,22 @@ public class Tree return new RenderNodes(container.getChildren()); } - Object onMarkExpanded(String nodeId) + Object doMarkExpanded(@RequestParameter(NODE_ID) String nodeId) { expansionModel.markExpanded(model.getById(nodeId)); return new JSONObject(); } - Object onMarkCollapsed(String nodeId) + + Object doMarkCollapsed(@RequestParameter(NODE_ID) String nodeId) { expansionModel.markCollapsed(model.getById(nodeId)); return new JSONObject(); } - Object onSelect(String nodeId, @RequestParameter("t:selected") boolean selected) + Object doUpdateSelected(String nodeId, boolean selected) { TreeNode node = model.getById(nodeId); @@ -312,7 +328,9 @@ public class Tree final Object result = callback.getResult(); if (result != null) + { return result; + } return new JSONObject(); } @@ -320,7 +338,9 @@ public class Tree public TreeExpansionModel getDefaultTreeExpansionModel() { if (defaultTreeExpansionModel == null) + { defaultTreeExpansionModel = new DefaultTreeExpansionModel(); + } return defaultTreeExpansionModel; } http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/23a69ca7/tapestry-core/src/main/resources/org/apache/tapestry5/corelib/components/Tree.tml ---------------------------------------------------------------------- diff --git a/tapestry-core/src/main/resources/org/apache/tapestry5/corelib/components/Tree.tml b/tapestry-core/src/main/resources/org/apache/tapestry5/corelib/components/Tree.tml index e7cebc5..8be548a 100644 --- a/tapestry-core/src/main/resources/org/apache/tapestry5/corelib/components/Tree.tml +++ b/tapestry-core/src/main/resources/org/apache/tapestry5/corelib/components/Tree.tml @@ -1,11 +1,13 @@ -<t:container xmlns:t="http://tapestry.apache.org/schema/tapestry_5_3.xsd" xmlns:p="tapestry:parameter"> +<t:container xmlns:t="http://tapestry.apache.org/schema/tapestry_5_3.xsd"> - <div class="${containerClass}"> - <t:delegate to="renderRootNodes"/> - </div> + <div class="${containerClass}" + data-component-type="core/Tree" + data-tree-action-url="${treeActionLink}"> + <t:delegate to="renderRootNodes"/> + </div> - <t:block id="defaultRenderTreeNodeLabel"> - ${node.label} - </t:block> + <t:block id="defaultRenderTreeNodeLabel"> + ${node.label} + </t:block> </t:container> \ No newline at end of file
