001package gwt.material.design.client.ui;
002
003/*
004 * #%L
005 * GwtMaterial
006 * %%
007 * Copyright (C) 2015 GwtMaterialDesign
008 * %%
009 * Licensed under the Apache License, Version 2.0 (the "License");
010 * you may not use this file except in compliance with the License.
011 * You may obtain a copy of the License at
012 * 
013 *      http://www.apache.org/licenses/LICENSE-2.0
014 * 
015 * Unless required by applicable law or agreed to in writing, software
016 * distributed under the License is distributed on an "AS IS" BASIS,
017 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
018 * See the License for the specific language governing permissions and
019 * limitations under the License.
020 * #L%
021 */
022
023import com.google.gwt.dom.client.Document;
024import com.google.gwt.dom.client.Element;
025import com.google.gwt.dom.client.NodeList;
026import com.google.gwt.dom.client.Style.Unit;
027import com.google.gwt.event.dom.client.ChangeEvent;
028import com.google.gwt.event.dom.client.ChangeHandler;
029import com.google.gwt.event.dom.client.HasChangeHandlers;
030import com.google.gwt.event.shared.HandlerRegistration;
031import gwt.material.design.client.base.MaterialWidget;
032import gwt.material.design.client.base.HasError;
033import gwt.material.design.client.base.mixin.ErrorMixin;
034import gwt.material.design.client.constants.InputType;
035import gwt.material.design.client.ui.html.Paragraph;
036import gwt.material.design.client.ui.html.Span;
037
038//@formatter:off
039
040/**
041 * Material Range - a slider that initialize the minimum and maximum values.
042 *
043 * <h3>UiBinder Usage:</h3>
044 * <pre>
045 *{@code<m:MaterialRange value="2" min="20" max="50" value="25"/>}
046 * </pre>
047 *
048 * @author kevzlou7979
049 * @author Ben Dol
050 * @see <a href="http://gwt-material-demo.herokuapp.com/#forms">Material Range</a>
051 */
052//@formatter:on
053public class MaterialRange extends MaterialWidget implements HasChangeHandlers, HasError {
054
055    private Paragraph paragraph = new Paragraph();
056    private MaterialInput input = new MaterialInput();
057    private Span thumb = new Span();
058
059    private static String VALUE = "value";
060    private static String MAX = "max";
061    private static String MIN = "min";
062    private static String INPUT = "INPUT";
063    private MaterialLabel lblError = new MaterialLabel();
064
065    // cache the embedded range input element
066    private Element rangeElement = null;
067
068    private final ErrorMixin<MaterialRange, MaterialLabel> errorMixin = new ErrorMixin<>(this, lblError, null);
069
070    /**
071     * Creates a range
072     */
073    public MaterialRange() {
074        super(Document.get().createElement("form"));
075        getElement().setAttribute("action", "#");
076        lblError.setVisible(false);
077        paragraph.setStyleName("range-field");
078
079        input.setType(InputType.RANGE);
080        paragraph.add(input);
081
082        thumb.getElement().setClassName("thumb");
083        Span value = new Span();
084        value.getElement().setClassName("value");
085        thumb.add(value);
086
087        paragraph.add(thumb);
088        add(paragraph);
089
090        lblError.getElement().getStyle().setMarginTop(-10, Unit.PX);
091        add(lblError);
092    }
093
094    /**
095     * Creates a range with specified values
096     * @param min - start min value
097     * @param max - end max value
098     * @param value - default range value
099     */
100    public MaterialRange(Integer min, Integer max, Integer value) {
101        this();
102        setMin(min);
103        setMax(max);
104        setValue(value);
105    }
106
107    public void reset() {
108        setValue(getMin());
109        clearErrorOrSuccess();
110    }
111
112    /**
113     * Try to identify the embedded range elements input field (see ui xml)
114     * @return The found element or null if none found.
115     */
116    protected Element getRangeElement() {
117        if (rangeElement == null) {
118            NodeList<Element> elements = this.getElement().getElementsByTagName(INPUT);
119            if (elements != null && elements.getLength() > 0) {
120                rangeElement = elements.getItem(0);
121            }
122        }
123        return rangeElement;
124    }
125
126    /**
127     * Retrieve the Integer value from the given Attribute of the range element
128     * @param attribute The name of the attribute on the range element
129     * @return The Integer vaulue read from the given attribute or null
130     */
131    protected Integer getIntFromRangeElement(String attribute) {
132        Element ele = getRangeElement();
133        if(ele != null) {
134          return ele.getPropertyInt(attribute);
135        }
136        return null;
137    }
138
139    /**
140     * Set the given Integer value to the attribute of the range element.
141     */
142    protected void setIntToRangeElement(String attribute,Integer val) {
143        Element ele = getRangeElement();
144        if(ele != null) {
145            ele.setPropertyInt(attribute,val);
146        }
147    }
148
149    /**
150     * Read the current value
151     * @return The Integer value or null
152     */
153    public Integer getValue() {
154        return getIntFromRangeElement(VALUE);
155    }
156
157    /**
158     * Write the current value
159     * @param value value must be &gt;= min and &lt;= max
160     */
161    public void setValue(Integer value) {
162        if (value == null)return;
163        if (value < getMin())return;
164        if (value > getMax())return;
165        setIntToRangeElement(VALUE,value);
166    }
167
168    /**
169     * Read the min value
170     * @return The Integer or null
171     */
172    public Integer getMin() {
173        return getIntFromRangeElement(MIN);
174    }
175
176    /**
177     * Write the current min value
178     * @param min value must be &lt; max
179     */
180    public void setMin(Integer min) {
181        setIntToRangeElement(MIN,min);
182    }
183
184    /**
185     * Read the max value
186     * @return The Integer or null
187     */
188    public Integer getMax() {
189        return getIntFromRangeElement(MAX);
190    }
191
192    /**
193     * Write the current max value
194     * @param max value must be &gt; min
195     */
196    public void setMax(Integer max) {
197        setIntToRangeElement(MAX,max);
198    }
199
200    /**
201     * Register the ChangeHandler to become notified if the user changes the slider.
202     * The Handler is called when the user releases the mouse only at the end of the slide
203     * operation.
204     */
205    @Override
206    public HandlerRegistration addChangeHandler(final ChangeHandler handler) {
207        return addDomHandler(new ChangeHandler() {
208            @Override
209            public void onChange(ChangeEvent event) {
210                if(isEnabled()){
211                    handler.onChange(event);
212                }
213            }
214        }, ChangeEvent.getType());
215    }
216
217    @Override
218    public void setError(String error) {
219        errorMixin.setError(error);
220    }
221
222    @Override
223    public void setSuccess(String success) {
224        errorMixin.setSuccess(success);
225    }
226    
227    @Override
228    public void setHelperText(String helperText) {
229        errorMixin.setHelperText(helperText);
230    }
231
232    @Override
233    public void clearErrorOrSuccess() {
234        errorMixin.clearErrorOrSuccess();
235    }
236}