View Javadoc
1   /**
2    * This file Copyright (c) 2018 Magnolia International
3    * Ltd.  (http://www.magnolia-cms.com). All rights reserved.
4    *
5    *
6    * This file is dual-licensed under both the Magnolia
7    * Network Agreement and the GNU General Public License.
8    * You may elect to use one or the other of these licenses.
9    *
10   * This file is distributed in the hope that it will be
11   * useful, but AS-IS and WITHOUT ANY WARRANTY; without even the
12   * implied warranty of MERCHANTABILITY or FITNESS FOR A
13   * PARTICULAR PURPOSE, TITLE, or NONINFRINGEMENT.
14   * Redistribution, except as permitted by whichever of the GPL
15   * or MNA you select, is prohibited.
16   *
17   * 1. For the GPL license (GPL), you can redistribute and/or
18   * modify this file under the terms of the GNU General
19   * Public License, Version 3, as published by the Free Software
20   * Foundation.  You should have received a copy of the GNU
21   * General Public License, Version 3 along with this program;
22   * if not, write to the Free Software Foundation, Inc., 51
23   * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
24   *
25   * 2. For the Magnolia Network Agreement (MNA), this file
26   * and the accompanying materials are made available under the
27   * terms of the MNA which accompanies this distribution, and
28   * is available at http://www.magnolia-cms.com/mna.html
29   *
30   * Any modifications to this file must keep this entire header
31   * intact.
32   *
33   */
34  package info.magnolia.poc;
35  
36  import static com.vaadin.server.Sizeable.Unit.PERCENTAGE;
37  
38  import info.magnolia.icons.MagnoliaIcons;
39  import info.magnolia.poc.customer.Customer;
40  import info.magnolia.poc.customer.CustomerStatusRenderer;
41  import info.magnolia.poc.customer.CustomerService;
42  import info.magnolia.ui.theme.ResurfaceTheme;
43  import info.magnolia.ui.vaadin.actionbar.Actionbar;
44  import info.magnolia.ui.vaadin.dialog.BaseDialog;
45  import info.magnolia.ui.vaadin.gwt.client.actionbar.shared.ActionbarItem;
46  
47  import java.time.LocalDate;
48  import java.time.format.DateTimeFormatter;
49  import java.util.Arrays;
50  import java.util.List;
51  
52  import javax.servlet.annotation.WebServlet;
53  
54  import org.jsoup.Jsoup;
55  import org.jsoup.safety.Whitelist;
56  import org.vaadin.addons.ComboBoxMultiselect;
57  
58  import com.vaadin.addon.daterangefield.DateRangeField;
59  import com.vaadin.annotations.Theme;
60  import com.vaadin.annotations.Title;
61  import com.vaadin.annotations.VaadinServletConfiguration;
62  import com.vaadin.annotations.Widgetset;
63  import com.vaadin.data.provider.DataProvider;
64  import com.vaadin.data.provider.ListDataProvider;
65  import com.vaadin.icons.VaadinIcons;
66  import com.vaadin.server.ThemeResource;
67  import com.vaadin.server.VaadinRequest;
68  import com.vaadin.server.VaadinServlet;
69  import com.vaadin.shared.ui.ContentMode;
70  import com.vaadin.ui.Button;
71  import com.vaadin.ui.ComboBox;
72  import com.vaadin.ui.Component;
73  import com.vaadin.ui.CssLayout;
74  import com.vaadin.ui.FormLayout;
75  import com.vaadin.ui.Grid;
76  import com.vaadin.ui.HorizontalLayout;
77  import com.vaadin.ui.Image;
78  import com.vaadin.ui.Label;
79  import com.vaadin.ui.Panel;
80  import com.vaadin.ui.TabSheet;
81  import com.vaadin.ui.TextArea;
82  import com.vaadin.ui.TextField;
83  import com.vaadin.ui.TreeGrid;
84  import com.vaadin.ui.UI;
85  import com.vaadin.ui.VerticalLayout;
86  import com.vaadin.ui.components.grid.ItemClickListener;
87  import com.vaadin.ui.renderers.HtmlRenderer;
88  import com.vaadin.ui.themes.ValoTheme;
89  
90  @Theme("poctheme")
91  @Title("Magnolia 6 Resurface")
92  @Widgetset("info.magnolia.poc.Widgetset")
93  public class LayoutUI extends UI {
94  
95      @WebServlet(value = "/layout/*", asyncSupported = true)
96      @VaadinServletConfiguration(productionMode = false, ui = LayoutUI.class)
97      public static class Servlet extends VaadinServlet {
98      }
99  
100     private HorizontalLayout header = new HorizontalLayout();
101     private CssLayout viewPort = new CssLayout();
102 
103     @Override
104     protected void init(VaadinRequest request) {
105         header.setStyleName("header");
106         header.setSpacing(false);
107 
108         // Create logo
109         CssLayout logoWrapper = createLogoComponent();
110 
111         // Create finder bar
112         CssLayout finderBarContainer = new CssLayout();
113         finderBarContainer.setStyleName("periscope-wrapper");
114 
115         Component finderBar = createFinderBar();
116 
117         finderBarContainer.addComponent(finderBar);
118 
119         // Create tasks indicator
120         CssLayout tasksWrapper = new CssLayout();
121         tasksWrapper.setStyleName("tasks-wrapper");
122         Component taskIndicator = createIndicatorComponent("Tasks", "23", "green");
123         tasksWrapper.addComponent(taskIndicator);
124 
125         // Create message indicator
126         CssLayout messageWrapper = new CssLayout();
127         messageWrapper.setStyleName("messages-wrapper");
128         Component messageIndicator = createIndicatorComponent("Messages", "02", "green");
129         messageWrapper.addComponent(messageIndicator);
130 
131         // Create profile component
132         Component profileWrapper = createProfileComponent();
133         profileWrapper.setStyleName("profile-wrapper");
134 
135         // Create banner component
136         Component bannerWrapper = createBannerComponent(tasksWrapper);
137         bannerWrapper.setStyleName("banner-wrapper");
138         bannerWrapper.setVisible(false);
139 
140         // Create periscope component
141         Component periscopeWrapper = periscopeComponent();
142         periscopeWrapper.setStyleName("periscope-filter-wrapper");
143         periscopeWrapper.setVisible(true);
144 
145         header.addComponents(logoWrapper, finderBarContainer, tasksWrapper, messageWrapper, profileWrapper);
146 
147         viewPort.setStyleName("viewport");
148         viewPort.setSizeFull();
149 
150         Component tabSheet = createTabSheetComponent();
151         viewPort.addComponents(bannerWrapper, periscopeWrapper, tabSheet);
152 
153 
154         final VerticalLayout layout = new VerticalLayout();
155         layout.setStyleName("main-wrapper");
156         layout.setSpacing(false);
157         layout.addComponents(header, viewPort);
158         layout.setMargin(false);
159         layout.setExpandRatio(header, 0f);
160         layout.setExpandRatio(viewPort, 1f);
161         layout.setSizeFull();
162 
163         setContent(layout);
164     }
165 
166     private Component periscopeComponent() {
167         CssLayout periscopeWrapper = new CssLayout();
168         VerticalLayout periscopeFilterWrapper = new VerticalLayout();
169         periscopeFilterWrapper.setWidth(100, Unit.PERCENTAGE);
170 
171         HorizontalLayout periscopeFilterHeader = new HorizontalLayout();
172         periscopeFilterHeader.setStyleName("periscope-filter-header-wrapper");
173         Button expandFilterOptionsButton = new Button(MagnoliaIcons.SELECT);
174         expandFilterOptionsButton.addStyleName(ResurfaceTheme.BUTTON_ICON);
175 
176         Label searchLabel = new Label("Refined search by");
177         searchLabel.setVisible(false);
178         CssLayout globalTokenLayout = new CssLayout();
179         periscopeFilterHeader.addComponents(expandFilterOptionsButton, searchLabel, globalTokenLayout);
180 
181         HorizontalLayout periscopeFilterOptions = new HorizontalLayout();
182         periscopeFilterOptions.setStyleName("periscope-filter-options-wrapper");
183         periscopeFilterOptions.setWidth(100, Unit.PERCENTAGE);
184         periscopeFilterOptions.setVisible(false);
185 
186         VerticalLayout supplierLayout = new VerticalLayout();
187         ComboBoxMultiselect<String> supplierFilter = new ComboBoxMultiselect<>("Type");
188         supplierFilter.setStyleGenerator(item -> "periscope-filter-item");
189         supplierFilter.setWidth(100, Unit.PERCENTAGE);
190         supplierFilter.setPlaceholder("Select or search...");
191         ListDataProvider<String> listDataProvider = DataProvider.ofItems("Pages", "Assets", "Stories", "Tours", "Documentation");
192         supplierFilter.setDataProvider(listDataProvider);
193         listDataProvider.setSortComparator(null);
194         supplierFilter.setItemIconGenerator(item -> {
195             switch (item) {
196             case "Tours":
197                 return MagnoliaIcons.LANGUAGE_APP;
198             default:
199                 return MagnoliaIcons.RESOURCE_FILES_APP;
200             }
201         });
202         supplierFilter.setPageLength(5);
203         CssLayout supplierTokensLayout = new CssLayout();
204         supplierTokensLayout.setStyleName("token-layout");
205         supplierFilter.addValueChangeListener(event -> populateTokenLayout(supplierTokensLayout, supplierFilter, true));
206         supplierLayout.addComponents(supplierFilter, supplierTokensLayout);
207 
208         VerticalLayout tagsLayout = new VerticalLayout();
209         ComboBoxMultiselect<String> tagsFilter = new ComboBoxMultiselect<>("Tags");
210         tagsFilter.setStyleGenerator(item -> "periscope-filter-item");
211         tagsFilter.setWidth(100, Unit.PERCENTAGE);
212         tagsFilter.setPlaceholder("Select or search...");
213         tagsFilter.setItems("adventure", "history", "mountains", "exotic", "food", "luxury", "unique", "all-inclusive", "drive", "orient", "wild-animals", "health", "snow", "spirituality", "islands", "sale", "city");
214         tagsFilter.setPageLength(5);
215         CssLayout tagsTokensLayout = new CssLayout();
216         tagsTokensLayout.setStyleName("token-layout");
217         tagsFilter.addValueChangeListener(event -> populateTokenLayout(tagsTokensLayout, tagsFilter, true));
218         tagsLayout.addComponents(tagsFilter, tagsTokensLayout);
219 
220         VerticalLayout editorLayout = new VerticalLayout();
221         ComboBoxMultiselect<String> editorFilter = new ComboBoxMultiselect<>("Last editor");
222         editorFilter.setStyleGenerator(item -> "periscope-filter-item");
223         editorFilter.setWidth(100, Unit.PERCENTAGE);
224         editorFilter.setPlaceholder("Select or search...");
225         editorFilter.setItems("Glenn Gomez", "Birdie Ball", "Cecelia Murphy", "Pauline Clarke", "Charlie Strickland", "Georgia Gaza");
226         editorFilter.setPageLength(5);
227         CssLayout editorTokensLayout = new CssLayout();
228         editorTokensLayout.setStyleName("token-layout");
229         editorFilter.addValueChangeListener(event -> populateTokenLayout(editorTokensLayout, editorFilter, true));
230         editorLayout.addComponents(editorFilter, editorTokensLayout);
231 
232         VerticalLayout dateLayout = new VerticalLayout();
233         DateRangeField dateRangeField = new DateRangeField("Last edited");
234         dateRangeField.setWidth(100, Unit.PERCENTAGE);
235         dateRangeField.setStyleName("date-range-field");
236         dateRangeField.getBeginDateField().setPlaceholder("Begin date");
237         dateRangeField.getEndDateField().setPlaceholder("End date");
238         LocalDate today = LocalDate.now();
239         dateRangeField.addShortcut("Today", field -> field.setRange(today, today));
240         dateRangeField.addShortcut("Last week", field -> field.setRange(today.minusWeeks(1), today));
241         dateRangeField.addShortcut("Last month", field -> field.setRange(today.minusMonths(1), today));
242         dateRangeField.addShortcut("Last year", field -> field.setRange(today.minusYears(1), today));
243         dateRangeField.addShortcutSeparator();
244         dateRangeField.addShortcut("Clear", DateRangeField::clear);
245         CssLayout dateTokenLayout = new CssLayout();
246         dateTokenLayout.setStyleName("token-layout");
247         dateRangeField.addValueChangeListener(event -> populateDateTokenLayout(dateTokenLayout, dateRangeField, true));
248         dateLayout.addComponents(dateRangeField, dateTokenLayout);
249 
250         expandFilterOptionsButton.addClickListener(click -> {
251             if (periscopeFilterOptions.isVisible()) {
252                 periscopeFilterOptions.setVisible(false);
253                 if (supplierTokensLayout.getComponentCount() < 1 && tagsTokensLayout.getComponentCount() < 1 && editorTokensLayout.getComponentCount() < 1 && dateTokenLayout.getComponentCount() < 1) {
254                     searchLabel.setVisible(false);
255                 }
256                 populateTokenLayout(globalTokenLayout, supplierFilter, true);
257                 populateTokenLayout(globalTokenLayout, tagsFilter, false);
258                 populateTokenLayout(globalTokenLayout, editorFilter, false);
259                 populateDateTokenLayout(globalTokenLayout, dateRangeField, false);
260                 globalTokenLayout.setVisible(true);
261             } else {
262                 periscopeFilterOptions.setVisible(true);
263                 searchLabel.setVisible(true);
264                 globalTokenLayout.setVisible(false);
265             }
266         });
267 
268         periscopeFilterOptions.addComponents(supplierLayout, tagsLayout, editorLayout, dateLayout);
269 
270         periscopeFilterWrapper.addComponents(periscopeFilterHeader, periscopeFilterOptions);
271         periscopeWrapper.addComponent(periscopeFilterWrapper);
272 
273         return periscopeWrapper;
274     }
275 
276     private void populateTokenLayout(CssLayout privateTokenLayout, ComboBoxMultiselect<String> field, boolean purgeLayout) {
277         if (purgeLayout) {
278             privateTokenLayout.removeAllComponents();
279         }
280         field.getValue().forEach(token -> {
281             Button tokenButton = new Button(token, click -> {
282                 privateTokenLayout.removeComponent(click.getButton());
283                 field.deselect(click.getButton().getCaption());
284             });
285             tokenButton.setIcon(MagnoliaIcons.CLOSE);
286             tokenButton.addStyleName("periscope-token-button");
287             privateTokenLayout.addComponent(tokenButton);
288         });
289     }
290 
291     private void populateDateTokenLayout(CssLayout dateTokenLayout, DateRangeField dateRangeField, boolean purgeLayout) {
292         if (purgeLayout) {
293             dateTokenLayout.removeAllComponents();
294         }
295         if (dateRangeField.getBeginDate() == null && dateRangeField.getEndDate() == null) {
296             return;
297         }
298         String label;
299         if (dateRangeField.getBeginDate() != null && dateRangeField.getEndDate() != null) {
300             label = dateRangeField.getBeginDate().format(DateTimeFormatter.ofPattern("dd.MM.yy"));
301             label += " - ";
302             label += dateRangeField.getEndDate().format(DateTimeFormatter.ofPattern("dd.MM.yy"));
303         } else if (dateRangeField.getBeginDate() != null) {
304             label = "> ";
305             label += dateRangeField.getBeginDate().format(DateTimeFormatter.ofPattern("dd.MM.yy"));
306         } else {
307             label = "< ";
308             label += dateRangeField.getEndDate().format(DateTimeFormatter.ofPattern("dd.MM.yy"));
309         }
310         Button tokenButton = new Button(label, click -> {
311             dateTokenLayout.removeComponent(click.getButton());
312             dateRangeField.clear();
313         });
314         tokenButton.setIcon(MagnoliaIcons.CLOSE);
315         tokenButton.addStyleName("periscope-token-button");
316         dateTokenLayout.addComponent(tokenButton);
317     }
318 
319     private CssLayout createLogoComponent() {
320         CssLayout logoWrapper = new CssLayout();
321         logoWrapper.setStyleName("logo-wrapper");
322 
323         ThemeResource resource = new ThemeResource("img/logo-magnolia.svg");
324         // Show the image in the application
325         Image image = new Image("Magnolia logo", resource);
326         image.addStyleName("header-component");
327         logoWrapper.addComponent(image);
328         return logoWrapper;
329     }
330 
331     private HorizontalLayout createProfileComponent() {
332         HorizontalLayout profileWrapper = new HorizontalLayout();
333         profileWrapper.setSpacing(false);
334         profileWrapper.setMargin(false);
335 
336         Label avatar = new Label();
337         avatar.setContentMode(ContentMode.HTML);
338         avatar.addStyleName("header-component");
339         avatar.setValue("<span class='indicator number icon-user-role'></span>");
340         profileWrapper.addComponent(avatar);
341 
342         Component profileComponent = createHeaderComponent("public-01", "INT");
343         profileWrapper.addComponent(profileComponent);
344         return profileWrapper;
345     }
346 
347     private Component createBannerComponent(CssLayout tasksWrapper) {
348         CssLayout bannerWrapper = new CssLayout();
349         bannerWrapper.setWidth(100, Unit.PERCENTAGE);
350         bannerWrapper.setHeight(80, Unit.PIXELS);
351 
352         VerticalLayout banner = new VerticalLayout();
353         banner.addStyleNames("banner", "info");
354         banner.setSpacing(false);
355         banner.setMargin(false);
356 
357         Label infoLabel = new Label("Info");
358         infoLabel.addStyleName("header");
359 
360         Label bodyLabel = new Label("Your license will expire in 15 days. Renew now.");
361         bodyLabel.addStyleName("body");
362 
363         Button closeButton = new Button();
364         closeButton.setCaptionAsHtml(true);
365         closeButton.setPrimaryStyleName("icon-close");
366         closeButton.addStyleName("close");
367         closeButton.addClickListener(e -> bannerWrapper.setVisible(false));
368 
369         banner.addComponents(infoLabel, bodyLabel, closeButton);
370         bannerWrapper.addComponent(banner);
371 
372         tasksWrapper.addLayoutClickListener(e -> {
373             if (!bannerWrapper.isVisible()) {
374                 bannerWrapper.setVisible(true);
375             }
376 
377             switch (banner.getStyleName()) {
378             case "banner info":
379                 banner.removeStyleName("info");
380                 banner.addStyleName("warning");
381                 infoLabel.setValue("Warning");
382                 bodyLabel.setValue("Your license will expire tomorrow. Magnolia will stop functioning if no valid license is found. Renew now.");
383                 break;
384             case "banner warning":
385                 banner.removeStyleName("warning");
386                 banner.addStyleName("error");
387                 infoLabel.setValue("Error");
388                 bodyLabel.setValue("The multipart stream ended unexpectedly. More.");
389                 break;
390             case "banner error":
391                 banner.removeStyleName("error");
392                 banner.addStyleName("info");
393                 infoLabel.setValue("Info");
394                 bodyLabel.setValue("Your license will expire in 15 days. Renew now.");
395                 break;
396             }
397         });
398 
399         return bannerWrapper;
400     }
401 
402     private HorizontalLayout createFinderBar() {
403         HorizontalLayout finderBar = new HorizontalLayout();
404         finderBar.addStyleName("header-component");
405         finderBar.setSpacing(false);
406         finderBar.setMargin(false);
407         finderBar.setSizeFull();
408         Button shellButton = new Button();
409         shellButton.addStyleName("btn-shell icon-appslauncher btn-appslauncher");
410         finderBar.addComponent(shellButton);
411         finderBar.setExpandRatio(shellButton, 0f);
412 
413         CssLayout searchField = new CssLayout();
414         searchField.setStyleName("search-field");
415         TextField textField = new TextField();
416         textField.addStyleNames("search-textfield", "heading-2");
417         textField.setPlaceholder("Type to find");
418         searchField.addComponent(textField);
419         Label searchIcon = new Label();
420         searchIcon.setStyleName("icon-search");
421         searchField.addComponent(searchIcon);
422 
423         finderBar.addComponent(searchField);
424         finderBar.setExpandRatio(searchField, 1f);
425 
426 
427         Button voiceButton = new Button();
428         voiceButton.addStyleName("btn-shell icon-target-app btn-voice");
429         finderBar.addComponent(voiceButton);
430         finderBar.setExpandRatio(voiceButton, 0f);
431 
432         return finderBar;
433     }
434 
435     private Component createIndicatorComponent(String label, String value, String status) {
436         return createHeaderComponent(label, value + "<span class='status icon-status-" + status + " color-" + status + "'></span>");
437     }
438 
439     private Component createHeaderComponent(String label, String value) {
440 
441         VerticalLayout tasksComponent = new VerticalLayout();
442         tasksComponent.setSpacing(false);
443         tasksComponent.setMargin(false);
444         tasksComponent.setStyleName("header-component");
445         Label taskNumber = new Label();
446         taskNumber.addStyleName("indicator heading-2");
447         taskNumber.setValue(value);
448         taskNumber.setContentMode(ContentMode.HTML);
449         Label taskLabel = new Label(label);
450         taskLabel.addStyleNames("text-tiny");
451         tasksComponent.addComponents(taskNumber, taskLabel);
452 
453         return tasksComponent;
454     }
455 
456     private Component createTabSheetComponent() {
457 
458         TabSheet tabSheetComponent = new TabSheet();
459         tabSheetComponent.addStyleName(ValoTheme.TABSHEET_FRAMED);
460         tabSheetComponent.addStyleName("apps");
461         tabSheetComponent.setSizeFull();
462 
463         Component browserSubapp = createBrowserAppComponent();
464         browserSubapp.addStyleName("app");
465         tabSheetComponent.addTab(browserSubapp, "Browser");
466         tabSheetComponent.setSizeFull();
467         browserSubapp.setSizeFull();
468 
469         Component detailAppWithVerticalLayout = createDetailAppWithVerticalLayout();
470         detailAppWithVerticalLayout.addStyleName("app");
471         tabSheetComponent.addTab(detailAppWithVerticalLayout, "Detail app w/ VerticalLayout");
472 
473         Component detailApp = createDetailAppWithBaseDialog();
474         detailApp.addStyleName("app");
475         tabSheetComponent.addTab(detailApp, "Detail app w/ BaseDialog", MagnoliaIcons.EDIT);
476 
477         final TextArea plainText3 = new TextArea();
478         plainText3.setWidth("100%");
479         plainText3.setRows(10);
480         plainText3.setValue("Soccis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. " +
481                 "Vestibulum id ligula porta felis euismod semper. " +
482                 "Nullam quis risus eget urna mollis ornare vel eu leo. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit.");
483         TabSheet.Tab tab3 = tabSheetComponent.addTab(plainText3, "Tab text 3");
484         tab3.setClosable(true);
485 
486         Component longLabelWithIconAndCloseButton = createDetailAppWithBaseDialog();
487         longLabelWithIconAndCloseButton.addStyleName("app");
488         TabSheet.Tab tab4 = tabSheetComponent.addTab(longLabelWithIconAndCloseButton, "Super long text so ellipsis is used", MagnoliaIcons.UPLOAD);
489         tab4.setClosable(true);
490 
491         return tabSheetComponent;
492     }
493 
494     private Component createBrowserAppComponent() {
495         VerticalLayout browser = new VerticalLayout();
496         browser.addStyleName("browser");
497         browser.setSizeFull();
498         browser.setMargin(false);
499 
500         VerticalLayout workbench = new VerticalLayout();
501         workbench.addStyleName("workbench");
502         workbench.setSizeFull();
503         workbench.setMargin(false);
504         Component toolBar = buildToolBar();
505         Component contentView = buildContentView();
506         Component statusBar = buildStatusBar();
507         workbench.addComponents(toolBar, contentView, statusBar);
508         workbench.setExpandRatio(toolBar, 0f);
509         workbench.setExpandRatio(contentView, 1f);
510 
511         Component actionbar = buildActionBar();
512 
513         HorizontalLayout workbenchWrapper = new HorizontalLayout();
514         workbenchWrapper.setSizeFull();
515         workbenchWrapper.addComponents(workbench, actionbar);
516         workbenchWrapper.setExpandRatio(workbench, 1f);
517         workbenchWrapper.setExpandRatio(actionbar, 0f);
518 
519         // TODO Status bar is now moved out of workbench
520         browser.addComponents(workbenchWrapper, statusBar);
521         browser.setExpandRatio(workbenchWrapper, 1f);
522         browser.setExpandRatio(statusBar, 0f);
523 
524 
525         return browser;
526     }
527 
528     private Component buildToolBar() {
529         HorizontalLayout toolbar = new HorizontalLayout();
530         toolbar.setWidth(100, PERCENTAGE);
531         toolbar.addStyleName("toolbar");
532         toolbar.setSpacing(true);
533 
534         CssLayout viewModes = new CssLayout();
535         viewModes.addStyleName("view-modes");
536 
537         Button treeBtn = new Button(MagnoliaIcons.VIEW_TREE);
538         treeBtn.addStyleNames(ResurfaceTheme.BUTTON_ICON, "link active");
539 
540         Button listBtn = new Button(MagnoliaIcons.VIEW_LIST);
541         listBtn.addStyleNames(ResurfaceTheme.BUTTON_ICON, "link");
542 
543         Button thumbnailBtn = new Button(MagnoliaIcons.VIEW_THUMBNAILS);
544         thumbnailBtn.addStyleNames(ResurfaceTheme.BUTTON_ICON, "link");
545 
546         viewModes.addComponents(treeBtn, listBtn, thumbnailBtn);
547 
548         CssLayout searchBox = new CssLayout();
549         searchBox.addStyleName("searchbox");
550         // TODO Does not have search box yet
551 //        TextField searchField = new TextField();
552 //        searchField.addStyleName("searchfield");
553 //        searchField.setSizeFull();
554 //        searchBox.addComponent(searchField);
555 
556         toolbar.addComponents(viewModes, searchBox);
557 
558         return toolbar;
559     }
560 
561     private Component buildContentView() {
562         Panel keyboardPanel = new Panel();
563         keyboardPanel.setSizeFull();
564         keyboardPanel.addStyleName("keyboard-panel");
565 
566         TreeGrid<Customer> treeGrid = new TreeGrid<>();
567 
568         treeGrid.setSizeFull();
569 
570         treeGrid.addColumn(customer -> {
571             String iconHtml;
572             if (customer.isPersisted()) {
573                 iconHtml = VaadinIcons.FOLDER_O.getHtml();
574             } else {
575                 iconHtml = VaadinIcons.FILE_O.getHtml();
576             }
577             return iconHtml + " " + Jsoup.clean(customer.getLastName(), Whitelist.simpleText());
578         }, new HtmlRenderer()).setCaption("Last Name").setExpandRatio(5);
579         treeGrid.addColumn(Customer::getFirstName).setCaption("First Name").setExpandRatio(2);
580         treeGrid.addColumn(customer -> customer.getStatus().name(), new CustomerStatusRenderer()).setCaption("Status").setExpandRatio(0);
581         treeGrid.addColumn(Customer::getBirthDate).setCaption("Date of Birth").setExpandRatio(2);
582         treeGrid.addColumn(Customer::getEmail).setCaption("Email").setExpandRatio(2);
583 
584         treeGrid.setSelectionMode(Grid.SelectionMode.MULTI);
585 
586         treeGrid.addItemClickListener((ItemClickListener<Customer>) event -> {
587             if (event.getSource().getSelectionModel().isSelected(event.getItem())) {
588                 event.getSource().getSelectionModel().deselect(event.getItem());
589             } else {
590                 event.getSource().getSelectionModel().select(event.getItem());
591             }
592         });
593 
594         // fetch list of items from service and assign it to Grid
595         // in this case it's a list of customers
596         CustomerService service = CustomerService.getInstance();
597         List<Customer> customers = service.findAll();
598         treeGrid.setItems(customers, Customer::getChildren);
599 
600         keyboardPanel.setContent(treeGrid);
601 
602         return keyboardPanel;
603     }
604 
605     private Component buildStatusBar() {
606         HorizontalLayout statusBar = new HorizontalLayout();
607         statusBar.addStyleName("statusbar");
608         statusBar.setWidth(100, PERCENTAGE);
609 
610         Label label = new Label("Profile images / Directors / Managing directors /");
611         label.setWidth(100, PERCENTAGE);
612 
613         statusBar.addComponent(label);
614 
615         return statusBar;
616     }
617 
618     private Component buildActionBar() {
619         Actionbar actionbar = new Actionbar();
620         actionbar.addSection("main", "Actions");
621         actionbar.addAction(new ActionbarItem("addAction", "Add action", "icon-add-item", "addActions"), "main");
622         actionbar.addAction(new ActionbarItem("deleteAction", "Delete action", "icon-delete", "addActions"), "main");
623         actionbar.addAction(new ActionbarItem("editAction", "Edit action", "icon-edit", "editActions"), "main");
624         CssLayout wrapper = new CssLayout();
625         wrapper.addComponent(actionbar);
626         wrapper.setWidthUndefined();
627         wrapper.setHeight(100, Unit.PERCENTAGE);
628         wrapper.addStyleName("actionbar");
629         return wrapper;
630     }
631 
632     private Component createDetailAppWithBaseDialog() {
633         HorizontalLayout detail = new HorizontalLayout();
634         detail.addStyleName("detail basedialog");
635         detail.setSizeFull();
636 
637         CssLayout detailView = new CssLayout();
638         detailView.addStyleName("detailview");
639         detailView.setSizeFull();
640         detail.addComponent(detailView);
641 
642         BaseDialog baseDialog = new BaseDialog();
643         baseDialog.setCaption("Asset");
644         baseDialog.setDialogDescription("This is description");
645         baseDialog.setDescriptionVisibility(true);
646         baseDialog.setSizeFull();
647 
648         baseDialog.setContent(createDetailForm());
649 
650         CssLayout footer = new CssLayout();
651         footer.setWidth(100, PERCENTAGE);
652         footer.addComponents(createActions());
653         baseDialog.setFooterToolbar(footer);
654 
655         Panel panel = new Panel();
656         panel.setSizeFull();
657         panel.setContent(baseDialog);
658 
659         detailView.addComponent(panel);
660 
661         return detail;
662     }
663 
664     private Component createDetailAppWithVerticalLayout() {
665         VerticalLayout detailLayout = new VerticalLayout();
666         detailLayout.addStyleName("detail");
667         detailLayout.setSizeFull();
668         detailLayout.setMargin(false);
669         detailLayout.setSpacing(false);
670 
671         Label title = new Label();
672         title.addStyleName("heading-1 title");
673         title.setValue("Asset");
674 
675         CssLayout header = new CssLayout();
676         header.addStyleName("detail-header");
677         header.addComponent(title);
678 
679         Component detailForm = createDetailForm();
680         detailForm.addStyleName("detail-content");
681 
682         CssLayout footer = new CssLayout();
683         footer.addStyleName("detail-footer");
684         footer.setWidth(100, PERCENTAGE);
685         footer.addComponents(createActions());
686 
687         detailLayout.addComponents(header, detailForm, footer);
688         detailLayout.setExpandRatio(header, 0f);
689         detailLayout.setExpandRatio(detailForm, 1f);
690         detailLayout.setExpandRatio(footer, 0f);
691 
692         return detailLayout;
693     }
694 
695     private Component createDetailForm() {
696         TabSheet tabSheetComponent = new TabSheet();
697         tabSheetComponent.setSizeFull();
698         tabSheetComponent.addStyleName(ValoTheme.TABSHEET_FRAMED);
699 
700         tabSheetComponent.addTab(createTab1(), "Meta Data");
701         tabSheetComponent.addTab(createTab2(), "Article Info");
702         tabSheetComponent.addTab(createTab3(), "Output channels");
703 
704         return tabSheetComponent;
705     }
706 
707     private FormLayout createTab1() {
708         FormLayout form = new FormLayout();
709         form.setMargin(true);
710         form.setSizeFull();
711 
712         TextField textField = new TextField("Asset name");
713 
714         TextField textFieldHover = new TextField("File name");
715         textFieldHover.setDescription("This is file name");
716 
717         TextField textFieldFocus = new TextField("Location");
718         textFieldFocus.setValue("Basel ");
719 
720         form.addComponents(textField,
721                 textFieldHover,
722                 textFieldFocus);
723 
724         form.iterator().forEachRemaining(
725                 component -> component.setWidth(100, PERCENTAGE)
726         );
727 
728         return form;
729     }
730 
731     private FormLayout createTab2() {
732         FormLayout form = new FormLayout();
733         form.setMargin(true);
734         form.setSizeFull();
735 
736         TextField disabledTextField = new TextField("Author");
737         disabledTextField.setEnabled(false);
738 
739         TextField textFieldValidation = new TextField("Birthday");
740 
741         form.addComponents(disabledTextField,
742                 textFieldValidation);
743 
744         form.iterator().forEachRemaining(
745                 component -> component.setWidth(100, PERCENTAGE)
746         );
747 
748         return form;
749     }
750 
751     private FormLayout createTab3() {
752         FormLayout form = new FormLayout();
753         form.setMargin(true);
754         form.setSizeFull();
755 
756         TextField textFieldWithPlaceHolder = new TextField("Title");
757         textFieldWithPlaceHolder.setPlaceholder("Add title here...");
758 
759         TextArea expandable = new TextArea("Description");
760         expandable.setRows(1);
761 
762         form.addComponents(textFieldWithPlaceHolder,
763                 expandable);
764 
765         form.iterator().forEachRemaining(
766                 component -> component.setWidth(100, PERCENTAGE)
767         );
768 
769         return form;
770     }
771 
772     private Component[] createActions() {
773         ComboBox<String> languageSelector = new ComboBox<>();
774         ListDataProvider<String> dataProvider = new ListDataProvider<>(Arrays.asList("English", "Việt Nam"));
775         languageSelector.setEmptySelectionAllowed(false);
776         languageSelector.setPlaceholder("Select...");
777         languageSelector.addStyleName("secondary-actions");
778         languageSelector.setDataProvider(dataProvider);
779 
780         Button saveButton = new Button("Save changes");
781         saveButton.addStyleName("commit primary-button primary-actions");
782 
783         Button downloadButton = new Button("Download", MagnoliaIcons.SHOW_VERSIONS);
784         downloadButton.addStyleName("download secondary-button primary-actions");
785 
786         Button cancelButton = new Button("Cancel");
787         cancelButton.addStyleName("cancel secondary-button primary-actions");
788 
789         return new Component[] { languageSelector, saveButton, downloadButton, cancelButton };
790     }
791 
792     @Override
793     public boolean equals(Object obj) {
794         return super.equals(obj);
795     }
796 
797     @Override
798     public int hashCode() {
799         return super.hashCode();
800     }
801 }