1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 package info.magnolia.admincentral.findbar.layout;
35
36 import info.magnolia.i18nsystem.SimpleTranslator;
37 import info.magnolia.icons.MagnoliaIcons;
38 import info.magnolia.ui.theme.ResurfaceTheme;
39
40 import java.time.LocalDate;
41 import java.time.format.DateTimeFormatter;
42 import java.util.Arrays;
43 import java.util.List;
44
45 import org.apache.commons.collections4.CollectionUtils;
46 import org.vaadin.addons.ComboBoxMultiselect;
47
48 import com.vaadin.addon.daterangefield.DateRangeField;
49 import com.vaadin.ui.Button;
50 import com.vaadin.ui.Component;
51 import com.vaadin.ui.CssLayout;
52 import com.vaadin.ui.HorizontalLayout;
53 import com.vaadin.ui.Label;
54 import com.vaadin.ui.VerticalLayout;
55
56
57
58
59
60 public class FilterLayout extends CssLayout {
61
62 private final DateRangeField dateField;
63 private final List<ComboBoxMultiselect<String>> fields;
64
65 private final HorizontalLayout periscopeFilterOptions;
66 private final SimpleTranslator i18n;
67 private CssLayout globalTokenLayout;
68 private Label searchLabel;
69
70 public FilterLayout(DateRangeField dateField, SimpleTranslator i18n, ComboBoxMultiselect<String>... fields) {
71 this.setStyleName("periscope-filter-wrapper");
72 this.setVisible(true);
73
74 this.fields = Arrays.asList(fields);
75
76 this.dateField = dateField;
77 this.i18n = i18n;
78
79 VerticalLayout periscopeFilter = new VerticalLayout();
80 periscopeFilter.setWidth(100, Unit.PERCENTAGE);
81 periscopeFilter.setMargin(false);
82 periscopeFilter.addStyleName("periscope-filter");
83
84 HorizontalLayout periscopeFilterHeader = initPeriscopeFilterHeader();
85
86 this.periscopeFilterOptions = initPeriscopeFilterOptions();
87
88 this.fields.forEach(field -> this.periscopeFilterOptions.addComponent(initFieldLayout(field)));
89
90 VerticalLayout editedFieldLayout = initRangeDateFieldLayout(dateField);
91 this.periscopeFilterOptions.addComponent(editedFieldLayout);
92
93 this.periscopeFilterOptions.forEach(component -> component.addStyleName("filter-section"));
94
95 periscopeFilter.addComponents(periscopeFilterHeader, periscopeFilterOptions);
96 this.addComponent(periscopeFilter);
97 }
98
99 private HorizontalLayout initPeriscopeFilterHeader() {
100 HorizontalLayout periscopeFilterHeader = new HorizontalLayout();
101 periscopeFilterHeader.setStyleName("periscope-filter-header-wrapper");
102 Button expandFilterOptionsButton = new Button(MagnoliaIcons.SEARCH_RESULT_FILTERS);
103 expandFilterOptionsButton.addStyleName(ResurfaceTheme.BUTTON_ICON);
104
105 searchLabel = new Label(i18n.translate("findbar.filter.refine"));
106 searchLabel.setVisible(false);
107 globalTokenLayout = new CssLayout() {
108
109 @Override
110 public void removeComponent(Component c) {
111 super.removeComponent(c);
112 if (getComponentCount() == 0) {
113 searchLabel.setVisible(false);
114 }
115 }
116 };
117
118 periscopeFilterHeader.addComponents(expandFilterOptionsButton, searchLabel, globalTokenLayout);
119
120 expandFilterOptionsButton.addClickListener(click -> {
121 boolean optionsVisible = !periscopeFilterOptions.isVisible();
122 boolean noFiltersSet = fields.stream().allMatch(field -> CollectionUtils.isEmpty(field.getValue()))
123 && dateField.getBeginDateField().getValue() == null
124 && dateField.getEndDateField().getValue() == null;
125
126 periscopeFilterOptions.setVisible(optionsVisible);
127 globalTokenLayout.setVisible(!optionsVisible);
128 globalTokenLayout.removeAllComponents();
129 if (!optionsVisible && !noFiltersSet) {
130 this.fields.forEach(field -> populateTokenLayout(globalTokenLayout, field));
131 populateDateTokenLayout(globalTokenLayout, dateField);
132 }
133 searchLabel.setVisible(optionsVisible || !noFiltersSet);
134 });
135 return periscopeFilterHeader;
136 }
137
138 private HorizontalLayout initPeriscopeFilterOptions() {
139 HorizontalLayout periscopeFilterOptions = new HorizontalLayout();
140 periscopeFilterOptions.setSpacing(false);
141 periscopeFilterOptions.setStyleName("periscope-filter-options-wrapper");
142 periscopeFilterOptions.setWidth(100, Unit.PERCENTAGE);
143 periscopeFilterOptions.setVisible(false);
144 return periscopeFilterOptions;
145 }
146
147 private VerticalLayout initFieldLayout(ComboBoxMultiselect<String> field) {
148 VerticalLayout fieldLayout = new VerticalLayout();
149 fieldLayout.setMargin(false);
150 field.setStyleGenerator(item -> "periscope-filter-item");
151 field.setWidth(100, Unit.PERCENTAGE);
152 field.setPlaceholder(i18n.translate("findbar.filter.placeholder"));
153 field.setPageLength(5);
154 CssLayout tokensLayout = new CssLayout();
155 tokensLayout.setStyleName("token-layout");
156 field.addValueChangeListener(event -> {
157 tokensLayout.removeAllComponents();
158 populateTokenLayout(tokensLayout, field);
159 });
160 fieldLayout.addComponents(field, tokensLayout);
161 return fieldLayout;
162 }
163
164 private VerticalLayout initRangeDateFieldLayout(DateRangeField field) {
165 VerticalLayout fieldLayout = new VerticalLayout();
166 fieldLayout.setMargin(false);
167 field.setWidth(100, Unit.PERCENTAGE);
168 field.setStyleName("date-range-field");
169 field.getBeginDateField().setPlaceholder(i18n.translate("findbar.filter.date.begin"));
170 field.getEndDateField().setPlaceholder(i18n.translate("findbar.filter.date.end"));
171 LocalDate today = LocalDate.now();
172 field.addShortcut(i18n.translate("findbar.filter.date.today"), item -> item.setRange(today, today));
173 field.addShortcut(i18n.translate("findbar.filter.date.lastweek"), item -> item.setRange(today.minusWeeks(1), today));
174 field.addShortcut(i18n.translate("findbar.filter.date.lastmonth"), item -> item.setRange(today.minusMonths(1), today));
175 field.addShortcut(i18n.translate("findbar.filter.date.lastyear"), item -> item.setRange(today.minusYears(1), today));
176 field.addShortcutSeparator();
177 field.addShortcut(i18n.translate("findbar.filter.clear"), DateRangeField::clear);
178 CssLayout tokensLayout = new CssLayout();
179 tokensLayout.setStyleName("token-layout");
180 field.addValueChangeListener(event -> {
181 tokensLayout.removeAllComponents();
182 populateDateTokenLayout(tokensLayout, field);
183 });
184 fieldLayout.addComponents(field, tokensLayout);
185 return fieldLayout;
186 }
187
188 private void populateTokenLayout(CssLayout privateTokenLayout, ComboBoxMultiselect<String> field) {
189 field.getValue().forEach(token -> {
190 Button tokenButton = new Button(token, click -> {
191 privateTokenLayout.removeComponent(click.getButton());
192 field.deselect(click.getButton().getCaption());
193 });
194 tokenButton.setIcon(MagnoliaIcons.CLOSE);
195 tokenButton.addStyleName("periscope-token-button");
196 privateTokenLayout.addComponent(tokenButton);
197 });
198 }
199
200 private void populateDateTokenLayout(CssLayout dateTokenLayout, DateRangeField dateRangeField) {
201 if (dateRangeField.getBeginDate() == null && dateRangeField.getEndDate() == null) {
202 return;
203 }
204 String label;
205 if (dateRangeField.getBeginDate() != null && dateRangeField.getEndDate() != null) {
206 label = dateRangeField.getBeginDate().format(DateTimeFormatter.ofPattern("dd.MM.yy"));
207 label += " - ";
208 label += dateRangeField.getEndDate().format(DateTimeFormatter.ofPattern("dd.MM.yy"));
209 } else if (dateRangeField.getBeginDate() != null) {
210 label = "> ";
211 label += dateRangeField.getBeginDate().format(DateTimeFormatter.ofPattern("dd.MM.yy"));
212 } else {
213 label = "< ";
214 label += dateRangeField.getEndDate().format(DateTimeFormatter.ofPattern("dd.MM.yy"));
215 }
216 Button tokenButton = new Button(label, click -> {
217 dateTokenLayout.removeComponent(click.getButton());
218 dateRangeField.clear();
219 });
220 tokenButton.setIcon(MagnoliaIcons.CLOSE);
221 tokenButton.addStyleName("periscope-token-button");
222 dateTokenLayout.addComponent(tokenButton);
223 }
224
225
226
227
228
229 public void updateGlobalTokenLayout() {
230 globalTokenLayout.removeAllComponents();
231 fields.forEach(field -> populateTokenLayout(globalTokenLayout, field));
232 populateDateTokenLayout(globalTokenLayout, dateField);
233 }
234 }