Skip to content

Commit c5e82c3

Browse files
committed
2 parents 1931799 + 1f13757 commit c5e82c3

File tree

10 files changed

+358
-7
lines changed

10 files changed

+358
-7
lines changed

README.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,11 @@ To see the demo, navigate to http://localhost:8080/
4848
- Drag and Drop Version. This version allows to Drag and Drop OrgChart nodes.
4949
### Version 2.0.2
5050
- Add support for node styling (e.g. change node colors)
51+
### Version 2.0.3
52+
- Update OrgChart library to version [2.1.3](https://github.com/dabeng/OrgChart/releases/tag/v2.1.3)
53+
- Add support for [node templates](https://rawgit.com/dabeng/OrgChart/master/demo/custom-template.html)
54+
- Fix options chartDepth and chartVerticalDepth
55+
- Fix export support in IE
5156

5257
## Roadmap
5358

@@ -89,7 +94,7 @@ Here is a simple example on how to try out the add-on component:
8994
item1.setChildren(Arrays.asList(item2, item3));
9095

9196
OrgChart orgChart = new OrgChart(item1);
92-
orgChart.setChartTitle("A Ttile");
97+
orgChart.setChartTitle("A Title");
9398
orgChart.setChartNodeContent("title");
9499
orgChart.setChartExportButton(true);
95100
orgChart.setZoom(true);

orgchart-addon-demo/src/main/java/com/flowingcode/vaadin/addons/orgchart/demo/DemoUI.java

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import com.flowingcode.vaadin.addons.orgchart.OrgChart;
2828
import com.flowingcode.vaadin.addons.orgchart.OrgChartItem;
2929
import com.flowingcode.vaadin.addons.orgchart.client.enums.ChartDirectionEnum;
30+
import com.flowingcode.vaadin.addons.orgchart.extra.TemplateLiteralRewriter;
3031
import com.vaadin.annotations.Theme;
3132
import com.vaadin.annotations.Title;
3233
import com.vaadin.annotations.VaadinServletConfiguration;
@@ -49,8 +50,13 @@ public static class Servlet extends VaadinServlet {
4950
@Override
5051
protected void init(VaadinRequest request) {
5152
// org chart 1
52-
OrgChart component1 = getExample1();
53-
component1.setChartTitle("My Organization Chart Demo - Example 1 - CHART EXPORT AS PICTURE AND DRAG & DROP");
53+
OrgChart component1 = getExample1();
54+
String nodeTemplate = "<div class='title'>${item.title}</div>"+
55+
"<div class='middle content'>${item.name}</div>" +
56+
"${item.data.mail?`<div class='custom content'>${item.data.mail}</div>`:''}";
57+
component1.setNodeTemplate("item", TemplateLiteralRewriter.rewriteFunction(nodeTemplate));
58+
59+
component1.setChartTitle("My Organization Chart Demo - Example 1 - CHART EXPORT AS PICTURE AND DRAG & DROP, CUSTOM TEMPLATE");
5460
component1.setChartNodeContent("title");
5561
component1.setChartExportButton(true);
5662
component1.setChartExpandCollapse(true);
@@ -69,18 +75,24 @@ protected void init(VaadinRequest request) {
6975

7076
public OrgChart getExample1() {
7177
OrgChartItem item1 = new OrgChartItem(1, "John Williams", "Director");
78+
item1.setData("mail", "[email protected]");
7279
item1.setClassName("blue-node");
7380
OrgChartItem item2 = new OrgChartItem(2, "Anna Thompson", "Administration");
81+
item2.setData("mail", "[email protected]");
7482
item2.setClassName("blue-node");
75-
OrgChartItem item3 = new OrgChartItem(3, "Timothy Jones", "Sub-Director");
83+
OrgChartItem item3 = new OrgChartItem(3, "Timothy Jones", "Sub-Director");
84+
item3.setData("mail", "[email protected]");
7685
item1.setChildren(Arrays.asList(item2, item3));
7786
OrgChartItem item4 = new OrgChartItem(4, "Louise Night", "Department 1");
87+
item4.setData("mail", "[email protected]");
7888
OrgChartItem item5 = new OrgChartItem(5, "John Porter", "Department 2");
89+
item5.setData("mail", "[email protected]");
7990
OrgChartItem item6 = new OrgChartItem(6, "Charles Thomas", "Department 3");
91+
item6.setData("mail", "[email protected]");
8092
item2.setChildren(Arrays.asList(item4, item5, item6));
8193
OrgChartItem item7 = new OrgChartItem(7, "Michael Wood", "Section 3.1");
8294
OrgChartItem item8 = new OrgChartItem(8, "Martha Brown", "Section 3.2");
83-
OrgChartItem item9 = new OrgChartItem(9, "Mary Parker", "Section 3.3");
95+
OrgChartItem item9 = new OrgChartItem(9, "Mary Parker", "Section 3.3");
8496
OrgChartItem item10 = new OrgChartItem(10, "Mary Williamson", "Section 3.4");
8597
item6.setChildren(Arrays.asList(item7, item8, item9, item10));
8698
return new OrgChart(item1);

orgchart-addon-demo/src/main/webapp/VAADIN/themes/demo/styles.scss

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,19 @@ $darkgreen: darken($green, 30%);
3838
.title {background-color: blue;}
3939
.content {border-color: blue;}
4040
}
41+
42+
.orgchart .node .content {
43+
& { border-radius: 0; border-bottom-width: 0; }
44+
&:first-of-type {
45+
border-top-width: 1px;
46+
border-top-right-radius: 4px;
47+
border-top-left-radius: 4px;
48+
}
49+
&:last-of-type{
50+
border-bottom-width: 1px;
51+
border-bottom-right-radius: 4px;
52+
border-bottom-left-radius: 4px;
53+
}
54+
}
55+
4156
}

orgchart-addon/pom.xml

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,13 +76,18 @@
7676
<scope>provided</scope>
7777
</dependency>
7878

79-
<!-- This can be replaced with TestNG or some other test framework supported by the surefire plugin -->
8079
<dependency>
8180
<groupId>junit</groupId>
8281
<artifactId>junit</artifactId>
8382
<version>4.8.1</version>
8483
<scope>test</scope>
8584
</dependency>
85+
<dependency>
86+
<groupId>org.assertj</groupId>
87+
<artifactId>assertj-core</artifactId>
88+
<version>2.5.0</version>
89+
</dependency>
90+
8691
<dependency>
8792
<groupId>com.fasterxml.jackson.core</groupId>
8893
<artifactId>jackson-databind</artifactId>
@@ -178,6 +183,15 @@
178183
<version>1.14</version>
179184
<configuration>
180185
<licenseName>apache_v2</licenseName>
186+
<failOnMissingHeader>true</failOnMissingHeader>
187+
<failOnNotUptodateHeader>true</failOnNotUptodateHeader>
188+
<excludes>
189+
<exclude>**/font-awesome.css</exclude>
190+
<exclude>**/html2canvas.js</exclude>
191+
<exclude>**/jquery-*.js</exclude>
192+
<exclude>**/jquery.orgchart.css</exclude>
193+
<exclude>**/jquery.orgchart.js</exclude>
194+
</excludes>
181195
</configuration>
182196
</plugin>
183197
</plugins>

orgchart-addon/src/main/java/com/flowingcode/vaadin/addons/orgchart/OrgChart.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,33 @@ private OrgChartItem getChildItemById(Integer id, List<OrgChartItem> children) {
198198
}
199199
return result;
200200
}
201+
202+
/**
203+
* Sets the template generation function used to customize the internal structure of nodes.
204+
* {@code functionBody} is the body of a javascript function that recieves one parameter
205+
* (the JSON datasoure representing a node) and returns an HTML snippet.
206+
* The name of this parameter is given by {@code parameterName}.
207+
*
208+
* Example:
209+
* <code>
210+
* setNodeTemplate("item","return '<span>'+item.name+'</span>';")
211+
* </code>
212+
* configures the following JS function as node template:
213+
* <code>
214+
* function(item) { return '<span>'+item.name+'</span>'; }
215+
* </code>
216+
*
217+
* {@linkplain OrgChartItem#setData(String, String) custom properties}
218+
* are accessible through {@code item.data}
219+
* <br>
220+
*
221+
* @param parameterName the name of the parameter of a javascript function
222+
* @param functionBody the body of a javascript function
223+
*/
224+
public void setNodeTemplate(String parameterName, String functionBody) {
225+
getState().nodeTemplateParam = parameterName;
226+
getState().nodeTemplate = functionBody;
227+
}
201228

202229
/**
203230
* Adds a {@link DragAndDropListener} to the component.
@@ -253,4 +280,5 @@ public interface DragAndDropListener extends Serializable {
253280

254281
public void onDragAndDropNode(DragAndDropEvent event);
255282
}
283+
256284
}

orgchart-addon/src/main/java/com/flowingcode/vaadin/addons/orgchart/OrgChartItem.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,11 @@
2222

2323
import java.io.Serializable;
2424
import java.util.ArrayList;
25+
import java.util.Collections;
26+
import java.util.HashMap;
2527
import java.util.List;
28+
import java.util.Map;
29+
import java.util.Optional;
2630

2731

2832
/**
@@ -44,6 +48,8 @@ public class OrgChartItem implements Serializable{
4448

4549
private List<OrgChartItem> children = new ArrayList<>();
4650

51+
private Map<String,String> data;
52+
4753
public OrgChartItem(Integer id, String name, String title) {
4854
super();
4955
this.id = id;
@@ -57,7 +63,26 @@ public OrgChartItem(OrgChartItem original) {
5763
orgChartCopy.addChildren(new OrgChartItem(child));
5864
}
5965
}
66+
67+
/**Return the map of {@linkplain #setData(String, String) custom properties}.*/
68+
public Map<String, String> getData() {
69+
return Optional.ofNullable(data).map(Collections::unmodifiableMap).orElse(Collections.emptyMap());
70+
}
6071

72+
/**Add or remove a custom property.
73+
* @param name the name of the custom property
74+
* @param value the value of the custom property */
75+
public void setData(String name, String value) {
76+
if (data==null) {
77+
data = new HashMap<>();
78+
}
79+
if (value!=null) {
80+
data.put(name, value);
81+
} else {
82+
data.remove(name);
83+
}
84+
}
85+
6186
public String getName() {
6287
return name;
6388
}

orgchart-addon/src/main/java/com/flowingcode/vaadin/addons/orgchart/client/OrgChartState.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,4 +68,8 @@ public class OrgChartState extends JavaScriptComponentState {
6868

6969
public String chartNodeId = ChartConstants.CHART_NODE_ID_DEFAULT;
7070

71+
public String nodeTemplate;
72+
73+
public String nodeTemplateParam = "item";
74+
7175
}
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
package com.flowingcode.vaadin.addons.orgchart.extra;
2+
3+
/**
4+
* ES6 template literal parser that rewrites the literal as an ES5 expression.
5+
* This class is experimental and subject to change, with no guarantee of completeness
6+
* (but it should work for simple expressions).
7+
*
8+
* @author Javier Godoy / Flowing Code
9+
*/
10+
public class TemplateLiteralRewriter {
11+
12+
//https://www.ecma-international.org/ecma-262/6.0/#sec-template-literal-lexical-components
13+
14+
public static String rewriteFunction(String s) {
15+
return "return "+rewrite(s)+";";
16+
}
17+
18+
// rewrites an ES6 template literal as an ES5 expression
19+
public static String rewrite(String s) {
20+
StringBuilder sb = new StringBuilder();
21+
sb.ensureCapacity(s.length()+1);
22+
rewriteTemplate(s, 0, sb, true);
23+
return sb.toString();
24+
}
25+
26+
/**Parse a Template literal in {@code s} starting at index {@code i} and rewrite it into {@code sb}.
27+
@param s The ES6 template literal
28+
@param i The index into {@code s} where the expression begins.
29+
@param sb The result buffer
30+
@throws IllegalArgumentException if the template literal is not closed with a backtick
31+
@return the index of the closing backtick that ends the Template literal
32+
*/
33+
private static int rewriteTemplate(String s, int i, StringBuilder sb, boolean implicit) {
34+
int begin = i;
35+
sb.append('"');
36+
//https://www.ecma-international.org/ecma-262/6.0/#sec-static-semantics-templatestrings
37+
outer: for (;i<s.length();i++) {
38+
char c = s.charAt(i);
39+
switch (c) {
40+
case '"':
41+
//escape quotes in TemplateCharacters since we are using them for the string literals
42+
sb.append('\\').append('"');
43+
break;
44+
case '\r':
45+
//The TV of TemplateCharacter :: LineTerminatorSequence is the TRV of LineTerminatorSequence.
46+
//The TRV of LineTerminatorSequence :: <CR><LF> is the sequence consisting of the code unit value 0x000A.
47+
//The TRV of LineTerminatorSequence :: <CR> is the code unit value 0x000A.
48+
if (i<s.length()-1 && s.charAt(i+1)=='\n') {
49+
++i;
50+
}
51+
//fallthrough
52+
case '\n':
53+
//The TRV of LineTerminatorSequence :: <LF> is the code unit value 0x000A.
54+
sb.append("\\n");
55+
continue;
56+
case '\u2028':
57+
//The TRV of LineTerminatorSequence :: <LS> is the code unit value 0x2028.
58+
sb.append("\\u2028");
59+
continue;
60+
case '\u2029':
61+
//The TRV of LineTerminatorSequence :: <PS> is the code unit value 0x2029.
62+
sb.append("\\u2029");
63+
continue;
64+
case '\\':
65+
if (++i==s.length()) {
66+
throw new IllegalArgumentException("Unterminated escape sequence at index "+i);
67+
}
68+
c = s.charAt(i);
69+
if (c=='\r' || c=='\n' || c=='\u2028' || c=='\u2029') {
70+
if (i<s.length()-1 && s.charAt(i+1)=='\n') {
71+
++i;
72+
}
73+
//LineTerminator :: <LF> | <CR> | <LS> | <PS>
74+
//The TV of LineContinuation :: \ LineTerminatorSequence is the empty code unit sequence.
75+
continue;
76+
} else {
77+
//EscapeSequence
78+
sb.append('\\').append(c);
79+
continue;
80+
}
81+
case '$': {
82+
if (i==s.length()-1 || s.charAt(i+1)!='{') {
83+
//this was a dangling dollar sign
84+
sb.append(c);
85+
continue;
86+
}
87+
88+
sb.append("\"+(");
89+
//expression substitution
90+
i = rewriteExpression(s,i+2,sb);
91+
sb.append(")+\"");
92+
continue;
93+
}
94+
case '`': {
95+
if (implicit) {
96+
throw new IllegalArgumentException("Unexpected backtick at index "+i);
97+
} else {
98+
break outer;
99+
}
100+
}
101+
default:
102+
sb.append(c);
103+
}
104+
}
105+
106+
if (!implicit && i>=s.length()) {
107+
throw new IllegalArgumentException("Unterminated template at index "+begin);
108+
}
109+
110+
//end of Template
111+
sb.append('"');
112+
return i;
113+
}
114+
115+
/**Parse a Expression in {@code s} starting at index {@code i} and rewrite it into {@code sb}.
116+
@param s The ES6 template literal
117+
@param i The index into {@code s} where the expression begins.
118+
@param sb The result buffer
119+
@return the index where the corresponding TemplateSubstitutionTail starts.
120+
*/
121+
private static int rewriteExpression(String s, int i, StringBuilder sb) {
122+
int begin = i;
123+
124+
if (begin==s.length()) {
125+
throw new IllegalArgumentException("Expected expression at index "+begin);
126+
}
127+
if (s.charAt(begin)=='}') {
128+
throw new IllegalArgumentException("Unexpected token } at index "+begin);
129+
}
130+
131+
for (;i<s.length();i++) {
132+
char c = s.charAt(i);
133+
switch (c) {
134+
case '}':
135+
//TemplateSubstitutionTail
136+
return i;
137+
case '`':
138+
//nested template literal
139+
sb.append("(");
140+
i = rewriteTemplate(s, i+1, sb, false);
141+
sb.append(")");
142+
continue;
143+
case '"':
144+
//fallthrough
145+
case '\'':
146+
int end = s.indexOf(c, i+1);
147+
if (end<0)
148+
throw new IllegalArgumentException("Unterminated string literal at index "+i);
149+
sb.append(s.substring(i,end)).append(c);
150+
i = end;
151+
continue;
152+
default:
153+
sb.append(c);
154+
}
155+
}
156+
157+
throw new IllegalArgumentException("Expected TemplateSubstitutionTail at index "+begin);
158+
}
159+
160+
}

0 commit comments

Comments
 (0)