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.client.ui;
021
022import com.google.gwt.dom.client.Document;
023import com.google.gwt.dom.client.Style.Unit;
024import com.google.gwt.event.logical.shared.ValueChangeEvent;
025import com.google.gwt.event.logical.shared.ValueChangeHandler;
026import com.google.gwt.event.shared.HandlerRegistration;
027import com.google.gwt.user.client.ui.HasValue;
028import gwt.material.design.client.base.AbstractValueWidget;
029import gwt.material.design.client.base.mixin.ErrorMixin;
030import gwt.material.design.client.constants.CssName;
031import gwt.material.design.client.constants.InputType;
032import gwt.material.design.client.ui.html.Label;
033import gwt.material.design.client.ui.html.Span;
034
035//@formatter:off
036
037/**
038 * Material Switch or other call it toggle - used for an alternative for checkbox
039 * <p>
040 * <h3>UiBinder Usage:</h3>
041 * <pre>
042 * {@code <m:MaterialSwitch value="true"/>
043 * <m:MaterialSwitch value="true" disabled="true"/>
044 * }
045 * </pre>
046 *
047 * @author kevzlou7979
048 * @see <a href="http://gwtmaterialdesign.github.io/gwt-material-demo/#switches">Material Switch</a>
049 * @see <a href="https://material.io/guidelines/components/selection-controls.html#selection-controls-switch">Material Design Specification</a>
050 */
051//@formatter:on
052public class MaterialSwitch extends AbstractValueWidget<Boolean> implements HasValue<Boolean> {
053
054    private MaterialInput input = new MaterialInput();
055    private MaterialLabel errorLabel = new MaterialLabel();
056    private Label label = new Label();
057    private Span span = new Span();
058    private Span onLabel = new Span();
059    private Span offLabel = new Span();
060
061    private ErrorMixin<AbstractValueWidget, MaterialLabel> errorMixin;
062
063    /**
064     * Creates a switch element
065     */
066    public MaterialSwitch() {
067        super(Document.get().createDivElement(), CssName.SWITCH);
068        span.setStyleName(CssName.LEVER);
069        input.setType(InputType.CHECKBOX);
070    }
071
072    public MaterialSwitch(String onLabel, String offLabel) {
073        this();
074        setOnLabel(onLabel);
075        setOffLabel(offLabel);
076    }
077
078    public MaterialSwitch(String onLabel, String offLabel, Boolean value) {
079        this(onLabel, offLabel);
080        setValue(value);
081    }
082
083    /**
084     * Creates a material switch with default value.
085     */
086    public MaterialSwitch(boolean value) {
087        this();
088        setValue(value);
089    }
090
091    @Override
092    protected void onLoad() {
093        super.onLoad();
094
095        label.add(offLabel);
096        label.add(input);
097        label.add(span);
098        add(label);
099        add(errorLabel);
100        errorLabel.getElement().getStyle().setMarginTop(16, Unit.PX);
101        label.add(onLabel);
102
103        // Register click handler here in order to have it at first position
104        // and therefore it will deal with clicks as first and setup the value
105        // right before others get notified.
106        registerHandler(addClickHandler(event -> {
107            event.preventDefault();
108            event.stopPropagation();
109        }));
110
111        registerHandler(addClickHandler(event -> setValue(!getValue(), true)));
112    }
113
114    @Override
115    public void setEnabled(boolean enabled) {
116        super.setEnabled(enabled);
117        span.setEnabled(enabled);
118        input.setEnabled(enabled);
119    }
120
121    /**
122     * Set the value of switch component.
123     */
124    @Override
125    public void setValue(Boolean value, boolean fireEvents) {
126        boolean oldValue = getValue();
127        if (value) {
128            input.getElement().setAttribute("checked", "true");
129        } else {
130            input.getElement().removeAttribute("checked");
131        }
132
133        if (fireEvents && oldValue != value) {
134            ValueChangeEvent.fire(this, getValue());
135        }
136    }
137
138    @Override
139    public void setValue(Boolean value) {
140        setValue(value, false);
141    }
142
143    /**
144     * Gets the value of switch component.
145     */
146    @Override
147    public Boolean getValue() {
148        return input.getElement().hasAttribute("checked");
149    }
150
151    /**
152     * @return the input
153     */
154    public MaterialInput getInput() {
155        return input;
156    }
157
158    /**
159     * @param input the input to set
160     */
161    public void setInput(MaterialInput input) {
162        this.input = input;
163    }
164
165    /**
166     * @return the span
167     */
168    public Span getSpan() {
169        return span;
170    }
171
172    /**
173     * @param span the span to set
174     */
175    public void setSpan(Span span) {
176        this.span = span;
177    }
178
179    /**
180     * @return the label
181     */
182    public Label getLabel() {
183        return label;
184    }
185
186    /**
187     * @param label the label to set
188     */
189    @Deprecated
190    public void setLabel(Label label) {
191        this.label = label;
192    }
193
194    @Override
195    public void setError(String error) {
196        getErrorMixin().setError(error);
197    }
198
199    @Override
200    public void setSuccess(String success) {
201        getErrorMixin().setSuccess(success);
202    }
203
204    @Override
205    public void setHelperText(String helperText) {
206        getErrorMixin().setHelperText(helperText);
207    }
208
209    @Override
210    public void clearErrorOrSuccess() {
211        getErrorMixin().clearErrorOrSuccess();
212    }
213
214    /**
215     * Set the On State Label of the switch component
216     */
217    public void setOnLabel(String label) {
218        onLabel.setText(label);
219    }
220
221    /**
222     * Set the Off State Label of the switch component
223     */
224    public void setOffLabel(String label) {
225        offLabel.setText(label);
226    }
227
228    public MaterialLabel getErrorLabel() {
229        return errorLabel;
230    }
231
232    public Span getOnLabel() {
233        return onLabel;
234    }
235
236    public Span getOffLabel() {
237        return offLabel;
238    }
239
240    @Override
241    public HandlerRegistration addValueChangeHandler(final ValueChangeHandler<Boolean> handler) {
242        return addHandler(handler, ValueChangeEvent.getType());
243    }
244
245    @Override
246    protected ErrorMixin<AbstractValueWidget, MaterialLabel> getErrorMixin() {
247        if (errorMixin == null) {
248            errorMixin = new ErrorMixin<>(this, errorLabel, null);
249        }
250        return errorMixin;
251    }
252}