Skip to content

Commit 5aa723e

Browse files
committed
🎨 add tabs layout support
add a new annotation ( @tab ) handle and group fields by tabs #20
1 parent 2a103be commit 5aa723e

File tree

3 files changed

+110
-10
lines changed

3 files changed

+110
-10
lines changed

demo/src/main/java/io/asfjava/ui/demo/screen/DemoForm.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,38 +7,49 @@
77
import io.asfjava.ui.core.form.Number;
88
import io.asfjava.ui.core.form.Password;
99
import io.asfjava.ui.core.form.RadioBox;
10+
import io.asfjava.ui.core.form.Tab;
1011
import io.asfjava.ui.core.form.TextArea;
1112
import io.asfjava.ui.core.form.TextField;
1213

1314
public class DemoForm implements Serializable {
1415

16+
@Tab(title = "Info", index = 1)
1517
@TextField(title = "First Name", placeHolder = "Your first name", description = "This is a description for your first name field")
1618
private String firstName;
1719

20+
@Tab(title = "Info", index = 1)
1821
@TextField(title = "Last Name", placeHolder = "Your last name")
1922
private String lastName;
2023

24+
@Tab(title = "Contact", index = 2)
2125
@TextField(title = "eMail", placeHolder = "Your email", pattern = "^\\S+@\\S+$", validationMessage = "Your mail must be in this format [email protected]", description = "This is Text Field with pattern and validation message")
2226
private String email;
2327

28+
@Tab(title = "Additional Info", index = 3)
2429
@Number(title = "Number of children", placeHolder = "Number of children", description = "This is a number")
2530
private Integer number;
2631

32+
@Tab(title = "Contact", index = 2)
2733
@Password(title = "Password", placeHolder = "Please set you password", description = "This is password")
2834
private String password;
2935

36+
@Tab(title = "Info", index = 1)
3037
@ComboBox(title = "Gender", titleMap = GenderTitleMap.class)
3138
private String gender;
3239

40+
@Tab(title = "Additional Info", index = 3)
3341
@ComboBox(title = "Currency", values = { "euro", "dollar" })
3442
private String currency;
3543

44+
@Tab(title = "Additional Info", index = 3)
3645
@RadioBox(title = "Civil State", titleMap = CivilStateTitelsMap.class)
3746
private String civilState;
3847

48+
@Tab(title = "Contact", index = 2)
3949
@TextArea(title = "Address", placeHolder = "Fill your address please", description = "This is textarea")
4050
private String address;
4151

52+
@Tab(title = "Additional Info", index = 3)
4253
@CheckBox(title = "Color", values = { "red", "bleu", "green" }, defaultvalue = "red")
4354
private String color;
4455

@@ -101,7 +112,6 @@ public String getCivilState() {
101112
public void setCivilState(String civilState) {
102113
this.civilState = civilState;
103114
}
104-
105115

106116
public String getCurrency() {
107117
return currency;
@@ -111,6 +121,5 @@ public String getColor() {
111121
return color;
112122
}
113123

114-
115124
private static final long serialVersionUID = -5073515619469444978L;
116125
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package io.asfjava.ui.core.form;
2+
3+
import static java.lang.annotation.ElementType.FIELD;
4+
import static java.lang.annotation.RetentionPolicy.RUNTIME;
5+
6+
import java.lang.annotation.Retention;
7+
import java.lang.annotation.Target;
8+
9+
@Retention(RUNTIME)
10+
@Target(FIELD)
11+
public @interface Tab {
12+
String title();
13+
14+
int index();
15+
16+
}

src/main/java/io/asfjava/ui/core/schema/UiFormSchemaGenerator.java

Lines changed: 83 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,46 +3,121 @@
33
import java.io.Serializable;
44
import java.lang.annotation.Annotation;
55
import java.lang.reflect.Field;
6+
import java.util.ArrayList;
67
import java.util.Arrays;
8+
import java.util.Comparator;
9+
import java.util.HashMap;
10+
import java.util.LinkedHashMap;
11+
import java.util.List;
12+
import java.util.Map;
13+
import java.util.function.Predicate;
714

815
import com.fasterxml.jackson.databind.JsonMappingException;
16+
import com.fasterxml.jackson.databind.JsonNode;
917
import com.fasterxml.jackson.databind.ObjectMapper;
1018
import com.fasterxml.jackson.databind.node.ArrayNode;
1119
import com.fasterxml.jackson.databind.node.ObjectNode;
1220
import com.fasterxml.jackson.module.jsonSchema.JsonSchema;
1321
import com.fasterxml.jackson.module.jsonSchema.JsonSchemaGenerator;
1422

1523
import io.asfjava.ui.core.FormDefinitionGeneratorFactory;
24+
import io.asfjava.ui.core.form.Tab;
1625
import io.asfjava.ui.dto.UiForm;
1726

1827
public final class UiFormSchemaGenerator {
1928

2029
private static UiFormSchemaGenerator INSTANCE;
2130

2231
public UiForm generate(Class<? extends Serializable> formDto) throws JsonMappingException {
32+
Field[] declaredFields = formDto.getDeclaredFields();
2333
ObjectMapper mapper = new ObjectMapper();
24-
JsonSchemaGenerator schemaGen = new JsonSchemaGenerator(mapper, new CustomSchemaFactoryWrapper());
25-
JsonSchema schema = schemaGen.generateSchema(formDto);
2634

27-
ArrayNode formDefinition = mapper.createArrayNode();
35+
JsonSchemaGenerator schemaGen = initSchemaGen(mapper);
36+
JsonSchema schema = generateSchema(formDto, schemaGen);
37+
Map<Field, JsonNode> nodes = initFieldFormDefinition(mapper, formDto, declaredFields);
38+
39+
handlerGroupedFields();
40+
41+
ObjectNode tabbedFields = handleTabbedFields(mapper, declaredFields, nodes);
2842

29-
Arrays.stream(formDto.getDeclaredFields()).forEach(field -> buildFormDefinition(mapper, formDefinition, field));
43+
ArrayNode formDefinition = mapper.createArrayNode();
44+
formDefinition.add(tabbedFields);
3045

3146
return new UiForm(schema, formDefinition);
3247
}
3348

34-
private void buildFormDefinition(ObjectMapper mapper, ArrayNode formDefinitions, Field field) {
49+
private void handlerGroupedFields() {
50+
// TODO Grouping fieldset must handle it
51+
52+
}
53+
54+
private ObjectNode handleTabbedFields(ObjectMapper mapper, Field[] declaredFields, Map<Field, JsonNode> nodes) {
55+
Predicate<? super Field> checkTabAnnotation = field -> field.isAnnotationPresent(Tab.class);
56+
Comparator<? super Field> tabIndexComparator = (field1, field2) -> Integer
57+
.compare(field1.getAnnotation(Tab.class).index(), field2.getAnnotation(Tab.class).index());
58+
59+
Map<String, List<JsonNode>> groupedFieldsByTab = new LinkedHashMap<>();
60+
61+
Arrays.stream(declaredFields).filter(checkTabAnnotation).sorted(tabIndexComparator)
62+
.forEach(field -> groupFieldsByTab(nodes, field, groupedFieldsByTab));
63+
64+
ArrayNode tabs = mapper.createArrayNode();
65+
66+
groupedFieldsByTab.entrySet().stream().forEachOrdered(tabElements -> {
67+
ObjectNode tabNode = mapper.createObjectNode();
68+
tabNode.put("title", tabElements.getKey());
69+
ArrayNode tabItems = mapper.createArrayNode();
70+
tabElements.getValue().stream().forEach(fieldNode -> tabItems.add(fieldNode));
71+
tabNode.put("items", tabItems);
72+
tabs.add(tabNode);
73+
});
74+
75+
ObjectNode tabsNode = mapper.createObjectNode();
76+
tabsNode.put("type", "tabs");
77+
tabsNode.put("tabs", tabs);
78+
return tabsNode;
79+
80+
}
81+
82+
private Map<Field, JsonNode> initFieldFormDefinition(ObjectMapper mapper, Class<? extends Serializable> formDto,
83+
Field[] declaredFields) {
84+
Map<Field, JsonNode> nodes = new HashMap<>();
85+
Arrays.stream(declaredFields).forEach(field -> buildFormDefinition(nodes, mapper, field));
86+
return nodes;
87+
}
88+
89+
private JsonSchema generateSchema(Class<? extends Serializable> formDto, JsonSchemaGenerator schemaGen)
90+
throws JsonMappingException {
91+
return schemaGen.generateSchema(formDto);
92+
}
93+
94+
private JsonSchemaGenerator initSchemaGen(ObjectMapper mapper) {
95+
return new JsonSchemaGenerator(mapper, new CustomSchemaFactoryWrapper());
96+
}
97+
98+
private void groupFieldsByTab(Map<Field, JsonNode> nodes, Field field, Map<String, List<JsonNode>> groupedFields) {
99+
Tab tab = field.getAnnotation(Tab.class);
100+
List<JsonNode> fieldsGroupedByTab = groupedFields.get(tab.title());
101+
if (fieldsGroupedByTab == null) {
102+
fieldsGroupedByTab = new ArrayList<>();
103+
groupedFields.put(tab.title(), fieldsGroupedByTab);
104+
}
105+
fieldsGroupedByTab.add(nodes.get(field));
106+
nodes.remove(field);
107+
}
108+
109+
private void buildFormDefinition(Map<Field, JsonNode> nodes, ObjectMapper mapper, Field field) {
35110
Arrays.stream(field.getAnnotations())
36-
.forEach(annotation -> buildFieldDefinition(field, annotation, mapper, formDefinitions));
111+
.forEach(annotation -> buildFieldDefinition(field, annotation, mapper, nodes));
37112
}
38113

39114
private void buildFieldDefinition(Field field, Annotation annotation, ObjectMapper mapper,
40-
ArrayNode formDefinitions) {
115+
Map<Field, JsonNode> nodes) {
41116
ObjectNode fieldFormDefinition = mapper.createObjectNode();
42117
FormDefinitionGeneratorFactory.getInstance().getGenerator(annotation.annotationType().getName())
43118
.ifPresent(generator -> {
44119
generator.generate(fieldFormDefinition, field);
45-
formDefinitions.add(fieldFormDefinition);
120+
nodes.put(field, fieldFormDefinition);
46121
});
47122
}
48123

0 commit comments

Comments
 (0)