View Javadoc
1   /**
2    * This file Copyright (c) 2021 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.ui.field;
35  
36  import info.magnolia.icons.MagnoliaIcons;
37  import info.magnolia.ui.preview.ItemPreviewComponent;
38  
39  import java.util.Objects;
40  import java.util.Optional;
41  
42  import com.vaadin.data.HasValue;
43  import com.vaadin.ui.Button;
44  import com.vaadin.ui.Component;
45  import com.vaadin.ui.CustomField;
46  import com.vaadin.ui.HorizontalLayout;
47  import com.vaadin.ui.NativeButton;
48  import com.vaadin.ui.VerticalLayout;
49  
50  
51  /**
52   * A custom field displaying an inner field and a button for bringing up a component enabling to choose an item.
53   */
54  public abstract class AbstractLinkField<T, P> extends CustomField<T> {
55  
56      static final String REMOVE_BUTTON = "remove-button";
57      static final String MAGNOLIA_BUTTON = "magnoliabutton";
58      static final String SELECT_NEW_BUTTON = "select-new-button";
59      static final String LINK_FIELD = "linkfield";
60  
61      private final VerticalLayout rootLayout = new VerticalLayout();
62      private final HorizontalLayout linkLayout = new HorizontalLayout();
63      private final Button selectButton = new NativeButton();
64      private final HasValue<T> hasValue;
65      private final Button removeButton = new Button(MagnoliaIcons.DELETE, event -> setValue(null));
66      private final Component innerComponent;
67  
68      private boolean editable;
69      private Component contentPreview;
70      private String buttonCaptionNew;
71  
72      AbstractLinkField(HasValue<T> innerComponent) {
73          this.hasValue = innerComponent;
74          this.innerComponent = (Component) innerComponent;
75      }
76  
77      @Override
78      protected Component initContent() {
79          addStyleName(LINK_FIELD);
80  
81          rootLayout.setMargin(false);
82          rootLayout.setSizeFull();
83          rootLayout.setSpacing(true);
84  
85          hasValue.setRequiredIndicatorVisible(false);
86          hasValue.addValueChangeListener(event -> doSetValue((T) event.getValue()));
87          innerComponent.setCaption(null);
88          innerComponent.setWidth(100, Unit.PERCENTAGE);
89  
90          linkLayout.setSizeFull();
91          linkLayout.addComponent(innerComponent);
92          linkLayout.setExpandRatio(innerComponent, 1);
93          linkLayout.setSpacing(false);
94  
95          if (!isReadOnly()) {
96              removeButton.setVisible(getOptionalValue().isPresent());
97              removeButton.addStyleName(REMOVE_BUTTON);
98              linkLayout.addComponent(removeButton);
99  
100             selectButton.addStyleName(MAGNOLIA_BUTTON);
101             selectButton.setCaption(buttonCaptionNew);
102             selectButton.addStyleName(SELECT_NEW_BUTTON);
103             linkLayout.addComponent(selectButton);
104             linkLayout.setExpandRatio(selectButton, 0);
105         }
106 
107         rootLayout.addComponent(linkLayout);
108         updateComponents();
109 
110         return rootLayout;
111     }
112 
113     @Override
114     protected void doSetValue(T value) {
115         hasValue.setValue(value);
116         setRemoveButtonVisibility(value);
117         updateComponents();
118     }
119 
120     @Override
121     public T getValue() {
122         return hasValue.getValue();
123     }
124 
125     @Override
126     public boolean isEmpty() {
127         return Objects.equals(getValue(), getEmptyValue());
128     }
129 
130     public boolean isEditable() {
131         return editable;
132     }
133 
134     public void setEditable(boolean editable) {
135         this.editable = editable;
136     }
137 
138     public void setButtonCaptionNew(String buttonCaptionNew) {
139         this.buttonCaptionNew = buttonCaptionNew;
140     }
141 
142     public void setDisableOnClick(boolean disable) {
143         selectButton.setDisableOnClick(disable);
144     }
145 
146     public void addClickListener(Button.ClickListener buttonClickListener) {
147         selectButton.addClickListener(buttonClickListener);
148     }
149 
150     @Override
151     public void setEnabled(boolean enabled) {
152         super.setEnabled(enabled);
153         innerComponent.setEnabled(enabled);
154         selectButton.setEnabled(enabled);
155     }
156 
157     public void setContentPreview(Component contentPreviewComponent) {
158         if (contentPreview != null) {
159             rootLayout.removeComponent(contentPreview);
160         }
161         rootLayout.addComponentAsFirst(contentPreviewComponent);
162         contentPreview = contentPreviewComponent;
163     }
164 
165     public void setContentPreview(ItemPreviewComponent<P> contentPreviewComponent) {
166         hasValue.addValueChangeListener(event -> {
167             P item = getSelectedItem().orElse(null);
168             contentPreviewComponent.onValueChange(item);
169             contentPreviewComponent.setVisible(item != null);
170         });
171         setContentPreview((Component) contentPreviewComponent);
172     }
173 
174     @Override
175     public void setReadOnly(boolean readOnly) {
176         super.setReadOnly(readOnly);
177         hasValue.setReadOnly(readOnly);
178     }
179     /*
180      * Set inner field as read only if desired. In this case, remove the add button, else update the button label.
181      */
182     private void updateComponents() {
183         if (!isEditable() && !isEmpty()) {
184             setReadOnly(true);
185             if (linkLayout.getComponentIndex(selectButton) != -1) {
186                 linkLayout.removeComponent(selectButton);
187             }
188             if (linkLayout.getComponentIndex(removeButton) != -1) {
189                 linkLayout.removeComponent(removeButton);
190             }
191         }
192     }
193 
194     private void setRemoveButtonVisibility(T value) {
195         removeButton.setVisible(!Objects.equals(value, getEmptyValue()));
196     }
197 
198     @Override
199     public T getEmptyValue() {
200         return hasValue.getEmptyValue();
201     }
202 
203     public abstract Optional<P> getSelectedItem();
204 
205     public abstract void setSelectedItem(P item);
206 }