001/*
002 * #%L
003 * GwtMaterial
004 * %%
005 * Copyright (C) 2015 - 2017 GwtMaterialDesign
006 * %%
007 * Licensed under the Apache License, Version 2.0 (the "License");
008 * you may not use this file except in compliance with the License.
009 * You may obtain a copy of the License at
010 * 
011 *      http://www.apache.org/licenses/LICENSE-2.0
012 * 
013 * Unless required by applicable law or agreed to in writing, software
014 * distributed under the License is distributed on an "AS IS" BASIS,
015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016 * See the License for the specific language governing permissions and
017 * limitations under the License.
018 * #L%
019 */
020package gwt.material.design.addins.client.stepper;
021
022import com.google.gwt.dom.client.Document;
023import com.google.gwt.event.dom.client.ClickHandler;
024import com.google.gwt.event.logical.shared.HasSelectionHandlers;
025import com.google.gwt.event.logical.shared.SelectionEvent;
026import com.google.gwt.event.logical.shared.SelectionHandler;
027import com.google.gwt.event.shared.HandlerRegistration;
028import com.google.gwt.safehtml.shared.SafeHtmlUtils;
029import com.google.gwt.user.client.ui.Widget;
030import gwt.material.design.addins.client.base.constants.AddinsCssName;
031import gwt.material.design.addins.client.stepper.constants.State;
032import gwt.material.design.client.base.*;
033import gwt.material.design.client.base.mixin.ActiveMixin;
034import gwt.material.design.client.constants.Axis;
035import gwt.material.design.client.constants.CssName;
036import gwt.material.design.client.constants.IconType;
037import gwt.material.design.client.ui.MaterialIcon;
038import gwt.material.design.client.ui.html.Div;
039
040//@formatter:off
041
042/**
043 * Material Step is a child element of Material Stepper, sometimes called Stepper Item, used to indicate the active
044 * inactive items on the Stepper Component.
045 * <p>
046 * <h3>XML Namespace Declaration</h3>
047 * <pre>
048 * {@code
049 * xmlns:ma='urn:import:gwt.material.design.addins.client'
050 * }
051 * </pre>
052 * <p>
053 * <h3>UiBinder Usage:</h3>
054 * <pre>
055 * {@code
056 *   <ma:stepper.MaterialStep step="1" title="Name of Step 1">
057 *       <m:MaterialPanel width="100%" height="300px" backgroundColor="grey lighten-2"/>
058 *       <m:MaterialButton ui:field="btnContinue1" text="Continue to Step 2" grid="l4" marginTop="12" backgroundColor="blue" textColor="white" waves="DEFAULT"/>
059 *       <m:MaterialButton ui:field="btnPrev1" text="Cancel" grid="l4" marginTop="12" type="FLAT" waves="DEFAULT"/>
060 *   </ma:stepper.MaterialStep>
061 * }
062 * </pre>
063 *
064 * @author kevzlou7979
065 * @see <a href="http://gwtmaterialdesign.github.io/gwt-material-demo/#steppers">Material Steppers</a>
066 * @see <a href="https://material.io/guidelines/components/steppers.html">Material Design Specification</a>
067 */
068// @formatter:on
069public class MaterialStep extends MaterialWidget implements HasActive, HasTitle, HasError, HasAxis,
070        HasSelectionHandlers<MaterialStep> {
071
072    private int step;
073    private String title;
074    private String description = "";
075    private Div conCircle = new Div();
076    private Div conBody = new Div();
077    private Div divCircle = new Div();
078    private Div divLine = new Div();
079    private Div divTitle = new Div();
080    private Div divDescription = new Div();
081    private Div divBody = new Div();
082    private MaterialIcon iconError = new MaterialIcon(IconType.REPORT_PROBLEM);
083    private MaterialIcon iconSuccess = new MaterialIcon(IconType.CHECK_CIRCLE);
084    private ActiveMixin<MaterialStep> activeMixin;
085    private Axis axis = Axis.VERTICAL;
086    private State state;
087
088    public MaterialStep() {
089        super(Document.get().createDivElement(), AddinsCssName.STEP);
090
091        super.add(conCircle);
092        conCircle.add(divCircle);
093        conCircle.add(divLine);
094
095        super.add(conBody);
096        conBody.add(divTitle);
097        conBody.add(divBody);
098
099        divCircle.setStyleName(CssName.CIRCLE);
100        divLine.setStyleName(AddinsCssName.LINE);
101        divTitle.setStyleName(CssName.TITLE);
102        divBody.setStyleName(AddinsCssName.BODY);
103    }
104
105    public MaterialStep(String title, String description) {
106        this();
107        setTitle(title);
108        setDescription(description);
109    }
110
111    public MaterialStep(String title, String description, Integer step) {
112        this(title, description);
113        setStep(step);
114    }
115
116    @Override
117    protected void onLoad() {
118        super.onLoad();
119
120        ClickHandler handler = event -> {
121            if (isEnabled() && isVisible()) {
122                SelectionEvent.fire(MaterialStep.this, MaterialStep.this);
123            }
124        };
125        registerHandler(conCircle.addClickHandler(handler));
126        registerHandler(divTitle.addClickHandler(handler));
127        registerHandler(divDescription.addClickHandler(handler));
128    }
129
130    @Override
131    public void add(Widget child) {
132        divBody.add(child);
133    }
134
135    public int getStep() {
136        return step;
137    }
138
139    public void setStep(int step) {
140        this.step = step;
141        divCircle.getElement().setInnerSafeHtml(SafeHtmlUtils.fromString(String.valueOf(step)));
142    }
143
144    @Override
145    public void setTitle(String title) {
146        this.title = title;
147        divTitle.getElement().setInnerSafeHtml(SafeHtmlUtils.fromString(title));
148    }
149
150    public String getTitle() {
151        return title;
152    }
153
154    @Override
155    public void setDescription(String description) {
156        this.description = description;
157        divDescription.setStyleName(AddinsCssName.DESCRIPTION);
158        divDescription.getElement().setInnerSafeHtml(SafeHtmlUtils.fromString(description));
159        conBody.insert(divDescription, 1);
160    }
161
162    public String getDescription() {
163        return description;
164    }
165
166    @Override
167    public void setActive(boolean active) {
168        getActiveMixin().setActive(active);
169    }
170
171    @Override
172    public boolean isActive() {
173        return getActiveMixin().isActive();
174    }
175
176    @Override
177    public void setError(String error) {
178        removeStyleName(AddinsCssName.SUCCESS);
179        addStyleName(AddinsCssName.ERROR);
180        applyIconStatus(iconError, error);
181        state = State.ERROR;
182    }
183
184    @Override
185    public void setSuccess(String success) {
186        removeStyleName(AddinsCssName.ERROR);
187        addStyleName(AddinsCssName.SUCCESS);
188        applyIconStatus(iconSuccess, success);
189        state = State.SUCCESS;
190    }
191
192    @Override
193    public void setHelperText(String helperText) {
194        setDescription(helperText);
195    }
196
197    public State getState() {
198        return state;
199    }
200
201    @Override
202    public void clearErrorOrSuccess() {
203        iconError.removeFromParent();
204        iconSuccess.removeFromParent();
205        conCircle.insert(divCircle, 0);
206        removeStyleName(AddinsCssName.ERROR);
207        removeStyleName(AddinsCssName.SUCCESS);
208    }
209
210    protected void applyIconStatus(MaterialIcon icon, String description) {
211        iconError.removeFromParent();
212        iconSuccess.removeFromParent();
213        divCircle.removeFromParent();
214        conCircle.insert(icon, 0);
215        if (description != null) {
216            divDescription.getElement().setInnerSafeHtml(SafeHtmlUtils.fromString(description));
217        }
218    }
219
220    public Div getDivBody() {
221        return divBody;
222    }
223
224    public Div getConCircle() {
225        return conCircle;
226    }
227
228    public Div getConBody() {
229        return conBody;
230    }
231
232    public Div getDivCircle() {
233        return divCircle;
234    }
235
236    public Div getDivLine() {
237        return divLine;
238    }
239
240    public Div getDivTitle() {
241        return divTitle;
242    }
243
244    public Div getDivDescription() {
245        return divDescription;
246    }
247
248    @Override
249    public void setAxis(Axis axis) {
250        if (axis == null) {
251            axis = Axis.VERTICAL;
252        }
253        this.axis = axis;
254        switch (axis) {
255            case HORIZONTAL:
256                conCircle.add(divTitle);
257                conCircle.add(divLine);
258                conCircle.add(divDescription);
259                break;
260            case VERTICAL:
261                conBody.insert(divTitle, 0);
262                conBody.insert(divDescription, 1);
263                conCircle.add(divLine);
264
265                break;
266        }
267    }
268
269    @Override
270    public Axis getAxis() {
271        return axis;
272    }
273
274    public MaterialIcon getIconError() {
275        return iconError;
276    }
277
278    public MaterialIcon getIconSuccess() {
279        return iconSuccess;
280    }
281
282    protected ActiveMixin<MaterialStep> getActiveMixin() {
283        if (activeMixin == null) {
284            activeMixin = new ActiveMixin<>(this);
285        }
286        return activeMixin;
287    }
288
289    @Override
290    public HandlerRegistration addSelectionHandler(final SelectionHandler<MaterialStep> handler) {
291        return this.addHandler(new SelectionHandler<MaterialStep>() {
292            @Override
293            public void onSelection(SelectionEvent<MaterialStep> event) {
294                if (isEnabled()) {
295                    handler.onSelection(event);
296                }
297            }
298        }, SelectionEvent.getType());
299    }
300}