001package gwt.material.design.addins.client.timepicker; 002 003import com.google.gwt.dom.client.Document; 004import com.google.gwt.dom.client.Element; 005import com.google.gwt.dom.client.Style; 006import com.google.gwt.event.logical.shared.*; 007import com.google.gwt.event.shared.HandlerRegistration; 008import com.google.gwt.i18n.shared.DateTimeFormat; 009import com.google.gwt.user.client.DOM; 010import com.google.gwt.user.client.ui.HasValue; 011import gwt.material.design.addins.client.MaterialAddins; 012import gwt.material.design.client.MaterialDesignBase; 013import gwt.material.design.client.base.*; 014import gwt.material.design.client.base.mixin.ErrorMixin; 015import gwt.material.design.client.base.mixin.ToggleStyleMixin; 016import gwt.material.design.client.constants.*; 017import gwt.material.design.client.ui.MaterialIcon; 018import gwt.material.design.client.ui.MaterialInput; 019import gwt.material.design.client.ui.MaterialLabel; 020import gwt.material.design.client.ui.MaterialPanel; 021import gwt.material.design.client.ui.html.Label; 022 023import java.util.Date; 024 025/* 026 * #%L 027 * GwtMaterial 028 * %% 029 * Copyright (C) 2015 GwtMaterialDesign 030 * %% 031 * Licensed under the Apache License, Version 2.0 (the "License"); 032 * you may not use this file except in compliance with the License. 033 * You may obtain a copy of the License at 034 * 035 * http://www.apache.org/licenses/LICENSE-2.0 036 * 037 * Unless required by applicable law or agreed to in writing, software 038 * distributed under the License is distributed on an "AS IS" BASIS, 039 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 040 * See the License for the specific language governing permissions and 041 * limitations under the License. 042 * #L% 043 */ 044 045//@formatter:off 046 047/** 048 * Material Time Picker - provide a simple way to select a single value from a pre-determined set. 049 * 050 * <h3>XML Namespace Declaration</h3> 051 * <pre> 052 * {@code 053 * xmlns:ma='urn:import:gwt.material.design.addins.client' 054 * } 055 * </pre> 056 * 057 * <h3>UiBinder Usage:</h3> 058 * <pre> 059 * {@code <ma:timepicker.MaterialTimePicker placeholder="Time Arrival" />} 060 * </pre> 061 * @see <a href="http://gwtmaterialdesign.github.io/gwt-material-demo/#timepickers">Material Pickers</a> 062 * @author kevzlou7979 063 * @author Ben Dol 064 */ 065//@formatter:on 066public class MaterialTimePicker extends MaterialWidget implements HasError, HasPlaceholder, HasOrientation, 067 HasCloseHandlers<Date>, HasOpenHandlers<Date>, HasValue<Date>, HasIcon { 068 069 static { 070 if(MaterialAddins.isDebug()) { 071 MaterialDesignBase.injectDebugJs(MaterialTimePickerDebugClientBundle.INSTANCE.timepickerJsDebug()); 072 MaterialDesignBase.injectCss(MaterialTimePickerDebugClientBundle.INSTANCE.timepickerCssDebug()); 073 } else { 074 MaterialDesignBase.injectJs(MaterialTimePickerClientBundle.INSTANCE.timepickerJs()); 075 MaterialDesignBase.injectCss(MaterialTimePickerClientBundle.INSTANCE.timepickerCss()); 076 } 077 } 078 079 /** Wraps the actual input. */ 080 private MaterialPanel panel = new MaterialPanel(); 081 082 /** The input element for the time picker. */ 083 private MaterialInput input = new MaterialInput(); 084 085 /** Label to display errors messages. */ 086 private MaterialLabel lblError = new MaterialLabel(); 087 088 /** The current value held by the time picker. */ 089 private Date time; 090 091 private Label label = new Label(); 092 093 private MaterialIcon icon = new MaterialIcon(); 094 095 /** */ 096 private ToggleStyleMixin<MaterialInput> validMixin = new ToggleStyleMixin<>(this.input, "valid"); 097 098 private final ErrorMixin<MaterialTimePicker, MaterialLabel> errorMixin = new ErrorMixin<>(this, this.lblError, this.input); 099 100 private String placeholder; 101 private boolean autoClose; 102 private boolean hour24; 103 private Orientation orientation = Orientation.PORTRAIT; 104 105 106 public MaterialTimePicker() { 107 super(Document.get().createElement("div"), "timepicker", "input-field"); 108 this.input.setType(InputType.TEXT); 109 this.panel.add(this.input); 110 this.panel.add(label); 111 this.panel.add(this.lblError); 112 this.add(this.panel); 113 } 114 115 @Override 116 protected void onLoad() { 117 super.onLoad(); 118 this.input.getElement().setAttribute("type", "text"); 119 this.initTimePicker(); 120 } 121 122 /** 123 * Side effects: 124 * <ul> 125 * <li>Resets the time to <i>now<i></li> 126 * <li>Clears erros/success message</li> 127 * </ul> 128 */ 129 public void reset() { 130 this.setValue(new Date()); 131 this.clearErrorOrSuccess(); 132 } 133 134 /** 135 * @return the time 136 */ 137 public String getTime() { 138 return this.getTime(this.input.getElement()); 139 } 140 141 public boolean isAutoClose() { 142 return this.autoClose; 143 } 144 145 public void setAutoClose(boolean autoClose) { 146 this.autoClose = autoClose; 147 } 148 149 /** 150 * False (default) change to 24 hours system. 151 * 152 * @return <code>false</code> in case 12 hours mode is set; 153 * <code>true</code> otherwise. 154 */ 155 public boolean isHour24() { 156 return this.hour24; 157 } 158 159 /** 160 * 161 * @param hour24 162 */ 163 public void setHour24(boolean hour24) { 164 this.hour24 = hour24; 165 } 166 167 /** 168 * @return The placeholder text. 169 */ 170 @Override 171 public String getPlaceholder() { 172 return this.placeholder; 173 } 174 175 /** 176 * @param placeholder 177 * The placeholder text to set. 178 */ 179 @Override 180 public void setPlaceholder(String placeholder) { 181 this.placeholder = placeholder; 182 this.label.setText(placeholder); 183 } 184 185 /** 186 * @return The orientation. 187 */ 188 @Override 189 public Orientation getOrientation() { 190 return this.orientation; 191 } 192 193 /** 194 * @param orientation 195 * The orientation to set: Can be Horizontal or Vertical. 196 */ 197 @Override 198 public void setOrientation(Orientation orientation) { 199 this.orientation = orientation; 200 } 201 202 @Override 203 public void setError(String error) { 204 this.errorMixin.setError(error); 205 } 206 207 @Override 208 public void setSuccess(String success) { 209 this.errorMixin.setSuccess(success); 210 } 211 212 @Override 213 public void setHelperText(String helperText) { 214 this.errorMixin.setHelperText(helperText); 215 } 216 217 @Override 218 public void clearErrorOrSuccess() { 219 this.errorMixin.clearErrorOrSuccess(); 220 } 221 222 public void initTimePicker() { 223 this.initTimePicker(DOM.createUniqueId(), this.input.getElement(), this.getOrientation().getCssName(), this.isAutoClose(), this.isHour24()); 224 } 225 226 /** 227 * 228 * @param clockId 229 * The clock id for the lolliclock. 230 * @param e 231 * The HTML element serving as container for textual content. 232 * @param orientation 233 * The initial orientation. 234 * @param autoClose 235 * Autoclose <code>true</code> or <code>false</code> 236 * @param hour24 237 * Set this <true>true</code> for a 24 hours clock; 238 * <code>false</code> otherwise. 239 */ 240 protected native void initTimePicker(String clockId, Element e, String orientation, boolean autoClose, boolean hour24) /*-{ 241 var that = this; 242 $wnd.jQuery(document).ready(function() { 243 $wnd.jQuery(e).lolliclock({ 244 autoclose: autoClose, 245 orientation: orientation, 246 hour24: hour24, 247 uniqueId: clockId, 248 beforeShow: function() { 249 that.@gwt.material.design.addins.client.timepicker.MaterialTimePicker::beforeShow()(); 250 }, 251 afterShow: function() { 252 that.@gwt.material.design.addins.client.timepicker.MaterialTimePicker::afterShow()(); 253 }, 254 afterHide: function() { 255 var hour = $wnd.jQuery('#' + clockId).find('.lolliclock-hours').find('.lolliclock-time-new').html(); 256 var minutes = $wnd.jQuery('#' + clockId).find('.lolliclock-minutes').find('.lolliclock-time-new').html(); 257 var suffix = $wnd.jQuery('#' + clockId).find('.lolliclock-am-pm').html(); 258 var time = hour + ':' + minutes + " " + suffix; 259 that.@gwt.material.design.addins.client.timepicker.MaterialTimePicker::afterHide(*)(); 260 } 261 }); 262 $wnd.jQuery(e).blur(); 263 }); 264 }-*/; 265 266 267 /** 268 * Called after the lolliclock event <code>afterShow</code>. 269 */ 270 protected void beforeShow() { 271 272 this.input.getElement().blur(); 273 274 // Add class 'valid' for visual feedback. 275 this.validMixin.setOn(true); 276 } 277 278 /** 279 * Called after the lolliclock event <code>afterShow</code>. 280 */ 281 protected void afterShow() { 282 this.fireOpenEvent(); 283 } 284 285 /** 286 * Called after the lolliclock event <code>afterHide</code>. 287 * 288 * @param time 289 * The time given by lolliclock in 12-hours <code>hh:mm aa</code> 290 * format. 291 */ 292 protected void afterHide() { 293 294 String timeString = this.getTime(this.input.getElement()); 295 296 Date parsedDate = null; 297 298 if(timeString.equals("") == false && timeString != null) { 299 try { 300 if(this.hour24 == true) { 301 DateTimeFormat hour24DateTimeFormat = DateTimeFormat.getFormat("HH:mm"); 302 parsedDate = hour24DateTimeFormat.parse(timeString); 303 } else { 304 DateTimeFormat hour12DateTimeFormat = DateTimeFormat.getFormat("hh:mm aa"); 305 parsedDate = hour12DateTimeFormat.parse(timeString); 306 } 307 } catch(Exception e) { 308 // Silently catch parse errors 309 } 310 } 311 312 this.setValue(parsedDate); 313 314 // Remove class 'valid' after hide. 315 this.validMixin.setOn(false); 316 317 this.fireCloseEvent(); 318 } 319 320 protected native String getTime(Element e)/*-{ 321 return $wnd.jQuery(e).val(); 322 }-*/; 323 324 @Override 325 public void setEnabled(boolean enabled) { 326 this.input.setEnabled(enabled); 327 } 328 329 @Override 330 public HandlerRegistration addCloseHandler(final CloseHandler<Date> handler) { 331 return this.addHandler(new CloseHandler<Date>() { 332 @Override 333 public void onClose(CloseEvent<Date> event) { 334 if(isEnabled()){ 335 handler.onClose(event); 336 } 337 } 338 }, CloseEvent.getType()); 339 } 340 341 @Override 342 public HandlerRegistration addOpenHandler(final OpenHandler<Date> handler) { 343 return this.addHandler(new OpenHandler<Date>() { 344 @Override 345 public void onOpen(OpenEvent<Date> event) { 346 if(isEnabled()){ 347 handler.onOpen(event); 348 } 349 } 350 }, OpenEvent.getType()); 351 } 352 353 protected void fireCloseEvent() { 354 CloseEvent.fire(this, this.time); 355 } 356 357 protected void fireOpenEvent() { 358 OpenEvent.fire(this, this.time); 359 } 360 361 protected void fireValueChangeEvent() { 362 ValueChangeEvent.fire(this, this.time); 363 } 364 365 @Override 366 public void clear() { 367 this.clearTimePickerValue(this.input.getElement()); 368 } 369 370 protected native void clearTimePickerValue(Element e) /*-{ 371 $wnd.jQuery(e).val(''); 372 }-*/; 373 374 @Override 375 public HandlerRegistration addValueChangeHandler(final ValueChangeHandler<Date> handler) { 376 return this.addHandler(new ValueChangeHandler<Date>() { 377 @Override 378 public void onValueChange(ValueChangeEvent<Date> event) { 379 if(isEnabled()){ 380 handler.onValueChange(event); 381 } 382 } 383 }, ValueChangeEvent.getType()); 384 } 385 386 @Override 387 public Date getValue() { 388 return this.time; 389 } 390 391 @Override 392 public void setValue(Date time) { 393 this.setValue(time, true); 394 } 395 396 @Override 397 public void setValue(Date time, boolean fireEvents) { 398 399 if(this.time != null) { 400 if(this.time.equals(time)) { 401 return; 402 } 403 } 404 405 if(this.time == time) { 406 return; 407 } 408 409 this.time = time; 410 411 String timeString = null; 412 413 if(this.hour24 == true) { 414 DateTimeFormat hour24DateTimeFormat = DateTimeFormat.getFormat("HH:mm"); 415 timeString = hour24DateTimeFormat.format(time); 416 } else { 417 DateTimeFormat hour12DateTimeFormat = DateTimeFormat.getFormat("hh:mm aa"); 418 timeString = hour12DateTimeFormat.format(time); 419 } 420 421 this.setValue(this.input.getElement(), timeString); 422 423 if(fireEvents == true) { 424 this.fireValueChangeEvent(); 425 } 426 } 427 428 protected native void setValue(Element e, String time) /*-{ 429 $wnd.jQuery(e).val(time); 430 }-*/; 431 432 @Override 433 public MaterialIcon getIcon() { 434 return icon; 435 } 436 437 @Override 438 public void setIconType(IconType iconType) { 439 icon.setIconType(iconType); 440 icon.setIconPrefix(true); 441 lblError.setPaddingLeft(44); 442 panel.insert(icon, 0); 443 } 444 445 @Override 446 public void setIconPosition(IconPosition position) { 447 icon.setIconPosition(position); 448 } 449 450 @Override 451 public void setIconSize(IconSize size) { 452 icon.setIconSize(size); 453 } 454 455 @Override 456 public void setIconFontSize(double size, Style.Unit unit) { 457 icon.setIconFontSize(size, unit); 458 } 459 460 @Override 461 public void setIconColor(String iconColor) { 462 icon.setIconColor(iconColor); 463 } 464 465 @Override 466 public void setIconPrefix(boolean prefix) { 467 icon.setIconPrefix(prefix); 468 } 469 470 @Override 471 public boolean isIconPrefix() { 472 return icon.isIconPrefix(); 473 } 474}