
com.vaadin.flow.component.datetimepicker.DateTimePicker Maven / Gradle / Ivy
/*
* Copyright 2000-2024 Vaadin Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.vaadin.flow.component.datetimepicker;
import java.io.Serializable;
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.temporal.ChronoUnit;
import java.util.Locale;
import java.util.Objects;
import java.util.Optional;
import com.vaadin.flow.component.AbstractField;
import com.vaadin.flow.component.AbstractSinglePropertyField;
import com.vaadin.flow.component.AttachEvent;
import com.vaadin.flow.component.Focusable;
import com.vaadin.flow.component.Tag;
import com.vaadin.flow.component.datepicker.DatePicker.DatePickerI18n;
import com.vaadin.flow.component.dependency.JsModule;
import com.vaadin.flow.component.dependency.NpmPackage;
import com.vaadin.flow.component.shared.ClientValidationUtil;
import com.vaadin.flow.component.shared.HasAutoOpen;
import com.vaadin.flow.component.shared.HasClientValidation;
import com.vaadin.flow.component.shared.HasOverlayClassName;
import com.vaadin.flow.component.shared.HasThemeVariant;
import com.vaadin.flow.component.shared.HasValidationProperties;
import com.vaadin.flow.component.shared.InputField;
import com.vaadin.flow.component.shared.SlotUtils;
import com.vaadin.flow.component.shared.ValidationUtil;
import com.vaadin.flow.component.timepicker.StepsUtil;
import com.vaadin.flow.data.binder.HasValidator;
import com.vaadin.flow.data.binder.ValidationResult;
import com.vaadin.flow.data.binder.ValidationStatusChangeEvent;
import com.vaadin.flow.data.binder.ValidationStatusChangeListener;
import com.vaadin.flow.data.binder.Validator;
import com.vaadin.flow.function.SerializableFunction;
import com.vaadin.flow.internal.JsonSerializer;
import com.vaadin.flow.shared.Registration;
@Tag("vaadin-date-picker")
class DateTimePickerDatePicker
extends com.vaadin.flow.component.datepicker.DatePicker {
@Override
protected void validate() {
// Should not change invalid state
}
void passThroughPresentationValue(LocalDate newPresentationValue) {
super.setPresentationValue(newPresentationValue);
if (valueEquals(newPresentationValue, getEmptyValue())
&& isInputValuePresent()) {
// Clear the input element from possible bad input.
getElement().executeJs("this.inputElement.value = ''");
getElement().setProperty("_hasInputValue", false);
}
}
@Override
protected boolean isInputValuePresent() {
return super.isInputValuePresent();
}
}
@Tag("vaadin-time-picker")
class DateTimePickerTimePicker
extends com.vaadin.flow.component.timepicker.TimePicker {
@Override
protected void validate() {
// Should not change invalid state
}
void passThroughPresentationValue(LocalTime newPresentationValue) {
super.setPresentationValue(newPresentationValue);
if (valueEquals(newPresentationValue, getEmptyValue())
&& isInputValuePresent()) {
// Clear the input element from possible bad input.
getElement().executeJs("this.inputElement.value = ''");
getElement().setProperty("_hasInputValue", false);
}
}
@Override
protected boolean isInputValuePresent() {
return super.isInputValuePresent();
}
}
/**
* Date Time Picker is an input field for selecting both a date and a time. The
* date and time can be entered directly using a keyboard in the format of the
* current locale or through the Date Time Picker’s two overlays. The overlays
* open when their respective fields are clicked or any input is entered when
* the fields are focused.
*
* @author Vaadin Ltd
*/
@Tag("vaadin-date-time-picker")
@NpmPackage(value = "@vaadin/polymer-legacy-adapter", version = "24.4.14")
@JsModule("@vaadin/polymer-legacy-adapter/style-modules.js")
@NpmPackage(value = "@vaadin/date-time-picker", version = "24.4.14")
@JsModule("@vaadin/date-time-picker/src/vaadin-date-time-picker.js")
public class DateTimePicker
extends AbstractSinglePropertyField
implements Focusable, HasAutoOpen, HasClientValidation,
InputField, LocalDateTime>,
HasOverlayClassName, HasThemeVariant,
HasValidationProperties, HasValidator {
private final DateTimePickerDatePicker datePicker = new DateTimePickerDatePicker();
private final DateTimePickerTimePicker timePicker = new DateTimePickerTimePicker();
private DatePickerI18n i18n;
private DateTimePickerI18n dateTimePickerI18n;
private Locale locale;
private final static SerializableFunction PARSER = s -> {
return s == null || s.isEmpty() ? null : LocalDateTime.parse(s);
};
private final static SerializableFunction FORMATTER = d -> {
return d == null ? "" : d.truncatedTo(ChronoUnit.MILLIS).toString();
};
private LocalDateTime max;
private LocalDateTime min;
private boolean required;
private boolean manualValidationEnabled = false;
/**
* Default constructor.
*/
public DateTimePicker() {
this((LocalDateTime) null);
}
/**
* Convenience constructor to create a date time picker with a label.
*
* @param label
* the label describing the date time picker
* @see #setLabel(String)
*/
public DateTimePicker(String label) {
this();
setLabel(label);
}
/**
* Convenience constructor to create a date time picker with a pre-selected
* date and time in current UI locale format and a label.
*
* @param label
* the label describing the date time picker
* @param initialDateTime
* the pre-selected date time in the picker
* @see #setValue(LocalDateTime)
* @see #setLabel(String)
*/
public DateTimePicker(String label, LocalDateTime initialDateTime) {
this(initialDateTime);
setLabel(label);
}
/**
* Convenience constructor to create a date time picker with a pre-selected
* date time in current UI locale format.
*
* @param initialDateTime
* the pre-selected date time in the picker
*/
public DateTimePicker(LocalDateTime initialDateTime) {
super("value", null, String.class, PARSER, FORMATTER);
if (initialDateTime != null) {
initialDateTime = sanitizeValue(initialDateTime);
setPresentationValue(initialDateTime);
synchronizeChildComponentValues(initialDateTime);
} else if (this.getElement().getProperty("value") == null) {
// Apply `null` as a value to force the client side `value` property
// to be initialized with an empty string. Having an empty string
// will prevent `ValueChangeEvent` which otherwise can be triggered
// in response to Polymer converting `null` to an empty string by
// itself.
// Only apply `null` if the element does not already have a value,
// which can be the case when binding to an existing element from a
// Lit template.
setPresentationValue(null);
}
SlotUtils.addToSlot(this, "date-picker", datePicker);
SlotUtils.addToSlot(this, "time-picker", timePicker);
// workaround for https://github.com/vaadin/flow/issues/3496
setInvalid(false);
addValueChangeListener(e -> validate());
addClientValidatedEventListener(e -> validate());
}
/**
* Convenience constructor to create a date time picker with a
* {@link ValueChangeListener}.
*
* @param listener
* the listener to receive value change events
* @see #addValueChangeListener(HasValue.ValueChangeListener)
*/
public DateTimePicker(
ValueChangeListener> listener) {
this();
addValueChangeListener(listener);
}
/**
* Convenience constructor to create a date time picker with a
* {@link ValueChangeListener} and a label.
*
*
* @param label
* the label describing the date time picker
* @param listener
* the listener to receive value change events
* @see #setLabel(String)
* @see #addValueChangeListener(HasValue.ValueChangeListener)
*/
public DateTimePicker(String label,
ValueChangeListener> listener) {
this(label);
addValueChangeListener(listener);
}
/**
* Convenience constructor to create a date time picker with a pre-selected
* date time in current UI locale format and a {@link ValueChangeListener}.
*
* @param initialDateTime
* the pre-selected date time in the picker
* @param listener
* the listener to receive value change events
* @see #setValue(LocalDateTime)
* @see #addValueChangeListener(HasValue.ValueChangeListener)
*/
public DateTimePicker(LocalDateTime initialDateTime,
ValueChangeListener> listener) {
this(initialDateTime);
addValueChangeListener(listener);
}
/**
* Convenience constructor to create a date time picker with a pre-selected
* date and time in current UI locale format, a {@link ValueChangeListener}
* and a label.
*
* @param label
* the label describing the date time picker
* @param initialDateTime
* the pre-selected date time in the picker
* @param listener
* the listener to receive value change events
* @see #setLabel(String)
* @see #setValue(LocalDateTime)
* @see #addValueChangeListener(HasValue.ValueChangeListener)
*/
public DateTimePicker(String label, LocalDateTime initialDateTime,
ValueChangeListener> listener) {
this(initialDateTime);
setLabel(label);
addValueChangeListener(listener);
}
/**
* Convenience constructor to create a date time picker with pre-selected
* date time and locale setup.
*
* @param initialDateTime
* the pre-selected date time in the picker
* @param locale
* the locale for the date time picker
*/
public DateTimePicker(LocalDateTime initialDateTime, Locale locale) {
this(initialDateTime);
setLocale(locale);
}
/**
* Sets the selected date and time value of the component. The value can be
* cleared by setting null.
*
*
* The value will be truncated to millisecond precision, as that is the
* maximum that the time picker supports. This means that
* {@link #getValue()} might return a different value than what was passed
* in.
*
* @param value
* the LocalDateTime instance representing the selected date and
* time, or null
*/
@Override
public void setValue(LocalDateTime value) {
LocalDateTime oldValue = getValue();
value = sanitizeValue(value);
super.setValue(value);
boolean isInputValuePresent = timePicker.isInputValuePresent()
|| datePicker.isInputValuePresent();
boolean isValueRemainedEmpty = valueEquals(oldValue, getEmptyValue())
&& valueEquals(value, getEmptyValue());
if (isValueRemainedEmpty && isInputValuePresent) {
// Clear the input elements from possible bad input.
synchronizeChildComponentValues(value);
fireEvent(new ClientValidatedEvent(this, false));
} else {
synchronizeChildComponentValues(value);
}
}
/**
* Sanitizes a LocalDateTime instance for to be used as internal value.
*
*
* Truncates value to millisecond precision, as that is the maximum that the
* time picker supports. This is also necessary to synchronize with the
* internal value of the Flow TimePicker, which truncates the value as well.
*
* @param value
* the LocalDateTime instance to sanitize, can be null
* @return sanitized LocalDateTime instance
*/
private LocalDateTime sanitizeValue(LocalDateTime value) {
if (value == null)
return null;
return value.truncatedTo(ChronoUnit.MILLIS);
}
private void synchronizeChildComponentValues(LocalDateTime value) {
if (value != null) {
datePicker.passThroughPresentationValue(value.toLocalDate());
timePicker.passThroughPresentationValue(value.toLocalTime());
} else {
datePicker.passThroughPresentationValue(null);
timePicker.passThroughPresentationValue(null);
}
}
@Override
public void setReadOnly(boolean readOnly) {
super.setReadOnly(readOnly);
// fixme(haprog) This override can probably be removed after we use a
// version which includes this fix:
// https://github.com/vaadin/vaadin-date-time-picker/pull/30
datePicker.setReadOnly(readOnly);
timePicker.setReadOnly(readOnly);
}
/**
* Sets the label for this field.
*
* @param label
* the String value to set
*/
@Override
public void setLabel(String label) {
getElement().setProperty("label", label == null ? "" : label);
}
/**
* Gets the label of this field.
*
* @return the {@code label} property of the date time picker
*/
@Override
public String getLabel() {
return getElement().getProperty("label");
}
/**
* Sets the aria-label for the component.
*
* @param ariaLabel
* the value to set as aria-label
*/
public void setAriaLabel(String ariaLabel) {
getElement().setProperty("accessibleName", ariaLabel);
}
/**
* Gets the aria-label of the component.
*
* @return an optional aria-label or an empty optional if no aria-label has
* been set
*/
public Optional getAriaLabel() {
return Optional.ofNullable(getElement().getProperty("accessibleName"));
}
/**
* Contains DateTimePicker internalization properties
*/
public static class DateTimePickerI18n implements Serializable {
private String dateLabel;
private String timeLabel;
public String getDateLabel() {
return dateLabel;
}
public void setDateLabel(String dateLabel) {
this.dateLabel = dateLabel;
}
public String getTimeLabel() {
return timeLabel;
}
public void setTimeLabel(String timeLabel) {
this.timeLabel = timeLabel;
}
}
/**
* Sets the accessible label for the date picker.
*
* The final value is a concatenation of the accessible label from
* DateTimePicker's {@link #getAriaLabel()} or {@link #getLabel()} and the
* given accessible label.
*
* @param dateLabel
* the value to be used as part of date picker aria-label.
*/
public void setDateAriaLabel(String dateLabel) {
if (dateTimePickerI18n == null) {
dateTimePickerI18n = new DateTimePickerI18n();
}
dateTimePickerI18n.setDateLabel(dateLabel);
getElement().setPropertyJson("i18n",
JsonSerializer.toJson(dateTimePickerI18n));
}
/**
* Gets the accessible label of the date picker.
*
* Note that this method will return the last value passed to
* {@link #setDateAriaLabel(String)}, not the value currently set on the
* `aria-label` attribute of the date picker input element.
*
* @return an optional label or an empty optional if no label has been set
*/
public Optional getDateAriaLabel() {
if (dateTimePickerI18n == null) {
return Optional.empty();
}
return Optional.ofNullable(dateTimePickerI18n.getDateLabel());
}
/**
* Sets the accessible label for the time picker.
*
* The final value is a concatenation of the accessible label from
* DateTimePicker's {@link #getAriaLabel()} or {@link #getLabel()} and the
* given accessible label.
*
* @param timeLabel
* the value to be used as part of time picker aria-label.
*/
public void setTimeAriaLabel(String timeLabel) {
if (dateTimePickerI18n == null) {
dateTimePickerI18n = new DateTimePickerI18n();
}
dateTimePickerI18n.setTimeLabel(timeLabel);
getElement().setPropertyJson("i18n",
JsonSerializer.toJson(dateTimePickerI18n));
}
/**
* Gets the accessible label of the time picker.
*
* Note that this method will return the last value passed to
* {@link #setTimeAriaLabel(String)}, not the value currently set on the
* `aria-label` attribute of the time picker input element.
*
* @return an optional label or an empty optional if no label has been set
*/
public Optional getTimeAriaLabel() {
if (dateTimePickerI18n == null) {
return Optional.empty();
}
return Optional.ofNullable(dateTimePickerI18n.getTimeLabel());
}
/**
* Sets a placeholder string for the date field.
*
* @param placeholder
* the String value to set
*/
public void setDatePlaceholder(String placeholder) {
datePicker.setPlaceholder(placeholder);
}
/**
* Gets the placeholder string of the date field.
*
* @return the {@code placeholder} property of the date picker
*/
public String getDatePlaceholder() {
return datePicker.getPlaceholder();
}
/**
* Set a placeholder string for the time field.
*
* @param placeholder
* the String value to set
*/
public void setTimePlaceholder(String placeholder) {
timePicker.setPlaceholder(placeholder);
}
/**
* Gets the placeholder string of the time field.
*
* @return the {@code placeholder} property of the time picker
*/
public String getTimePlaceholder() {
return timePicker.getPlaceholder();
}
/**
* Sets the {@code step} property of the time picker using duration. It
* specifies the intervals for the displayed items in the time picker
* dropdown and also the displayed time format.
*
* The set step needs to evenly divide a day or an hour and has to be larger
* than 0 milliseconds. By default, the format is {@code hh:mm} (same as *
* {@code Duration.ofHours(1)}
*
* If the step is less than 60 seconds, the format will be changed to
* {@code hh:mm:ss} and it can be in {@code hh:mm:ss.fff} format, when the
* step is less than 1 second.
*
* NOTE: If the step is less than 900 seconds, the dropdown is
* hidden.
*
* NOTE: changing the step to a larger duration can cause a new
* {@link com.vaadin.flow.component.HasValue.ValueChangeEvent} to be fired
* if some parts (eg. seconds) is discarded from the value.
*
* @param step
* the step to set, not {@code null} and should divide a day or
* an hour evenly
*/
public void setStep(Duration step) {
Objects.requireNonNull(step, "Step cannot be null");
getElement().setProperty("step",
StepsUtil.convertDurationToStepsValue(step));
timePicker.setStep(step);
}
/**
* Gets the step of the time picker.
*
* @return the {@code step} property from the picker, unit seconds
*/
public Duration getStep() {
// if step was not set by the user, then assume default value of the
// time picker web component
if (!getElement().hasProperty("step")) {
return StepsUtil.DEFAULT_WEB_COMPONENT_STEP;
}
double stepsValue = getElement().getProperty("step", 0.0);
return StepsUtil.convertStepsValueToDuration(stepsValue);
}
/**
* Show or hide the week numbers in the date picker. By default the week
* numbers are not shown.
*
* Set true to display ISO-8601 week numbers in the calendar.
*
* Note that displaying of week numbers is only supported when
* i18n.firstDayOfWeek is 1 (Monday).
*
* @param weekNumbersVisible
* the boolean value to set
* @see #setDatePickerI18n(DatePickerI18n)
* @see DatePickerI18n#setFirstDayOfWeek(int)
*/
public void setWeekNumbersVisible(boolean weekNumbersVisible) {
datePicker.setWeekNumbersVisible(weekNumbersVisible);
}
/**
* Get the state of {@code showWeekNumbers} property of the date picker.
*
* @return the {@code showWeekNumbers} property from the date picker
*/
public boolean isWeekNumbersVisible() {
return datePicker.isWeekNumbersVisible();
}
/**
* Set the Locale for the DateTimePicker. The displayed date and time will
* be matched to the format used in that locale.
*
* @param locale
* the locale to set to the DateTimePicker, cannot be null
*/
public void setLocale(Locale locale) {
Objects.requireNonNull(locale, "Locale must not be null.");
this.locale = locale;
datePicker.setLocale(locale);
timePicker.setLocale(locale);
}
/**
* Gets the Locale for this DateTimePicker
*
* @return the locale used for this DateTimePicker
*/
@Override
public Locale getLocale() {
if (locale != null) {
return locale;
} else {
return super.getLocale();
}
}
/**
* Synchronizes the theme attribute to the internal DatePicker and
* TimePicker components.
*/
private void synchronizeTheme() {
String theme = getThemeName();
theme = theme != null ? theme : "";
datePicker.getElement().setAttribute("theme", theme);
timePicker.getElement().setAttribute("theme", theme);
}
/**
* Adds a theme name to this component.
*
* @param themeName
* the theme name to add, not null
*/
@Override
public void addThemeName(String themeName) {
HasThemeVariant.super.addThemeName(themeName);
synchronizeTheme();
}
/**
* Removes a theme name from this component.
*
* @param themeName
* the theme name to remove, not null
* @return true
if the theme name was removed,
* false
if the theme list didn't contain the theme
* name
*/
@Override
public boolean removeThemeName(String themeName) {
boolean result = HasThemeVariant.super.removeThemeName(themeName);
synchronizeTheme();
return result;
}
/**
* Sets the theme names of this component. This method overwrites any
* previous set theme names.
*
* @param themeName
* a space-separated string of theme names to set, or empty
* string to remove all theme names
*/
@Override
public void setThemeName(String themeName) {
HasThemeVariant.super.setThemeName(themeName);
synchronizeTheme();
}
/**
* Sets or removes the given theme name for this component.
*
* @param themeName
* the theme name to set or remove, not null
* @param set
* true
to set the theme name, false
to
* remove it
*/
@Override
public void setThemeName(String themeName, boolean set) {
HasThemeVariant.super.setThemeName(themeName, set);
synchronizeTheme();
}
/**
* Adds one or more theme names to this component. Multiple theme names can
* be specified by using multiple parameters.
*
* @param themeNames
* the theme name or theme names to be added to the component
*/
@Override
public void addThemeNames(String... themeNames) {
HasThemeVariant.super.addThemeNames(themeNames);
synchronizeTheme();
}
/**
* Removes one or more theme names from component. Multiple theme names can
* be specified by using multiple parameters.
*
* @param themeNames
* the theme name or theme names to be removed from the component
*/
@Override
public void removeThemeNames(String... themeNames) {
HasThemeVariant.super.removeThemeNames(themeNames);
synchronizeTheme();
}
@Override
public Validator getDefaultValidator() {
return (value, context) -> checkValidity(value);
}
@Override
public Registration addValidationStatusChangeListener(
ValidationStatusChangeListener listener) {
return addClientValidatedEventListener(event -> listener
.validationStatusChanged(new ValidationStatusChangeEvent<>(this,
event.isValid())));
}
private ValidationResult checkValidity(LocalDateTime value) {
boolean hasNonParsableDatePickerValue = datePicker
.getValue() == datePicker.getEmptyValue()
&& datePicker.isInputValuePresent();
boolean hasNonParsableTimePickerValue = timePicker
.getValue() == timePicker.getEmptyValue()
&& timePicker.isInputValuePresent();
if (hasNonParsableDatePickerValue || hasNonParsableTimePickerValue) {
return ValidationResult.error("");
}
ValidationResult greaterThanMax = ValidationUtil
.checkGreaterThanMax(value, max);
if (greaterThanMax.isError()) {
return greaterThanMax;
}
ValidationResult smallerThanMin = ValidationUtil
.checkSmallerThanMin(value, min);
if (smallerThanMin.isError()) {
return smallerThanMin;
}
return ValidationResult.ok();
}
/**
* Gets the validity of the date time picker value.
*
* @return the current validity of the value.
*/
private boolean isInvalid(LocalDateTime value) {
var requiredValidation = ValidationUtil.checkRequired(required, value,
getEmptyValue());
return requiredValidation.isError() || checkValidity(value).isError();
}
@Override
public void setManualValidation(boolean enabled) {
this.manualValidationEnabled = enabled;
}
/**
* Performs server-side validation of the current value. This is needed
* because it is possible to circumvent the client-side validation
* constraints using browser development tools.
*/
protected void validate() {
if (!this.manualValidationEnabled) {
setInvalid(isInvalid(getValue()));
}
}
/**
* Sets the minimum date and time in the date time picker. Dates and times
* before that will be disabled in the popups.
*
* @param min
* the minimum date and time that is allowed to be set, or
* null
to remove any minimum constraints
*/
public void setMin(LocalDateTime min) {
getElement().setProperty("min", FORMATTER.apply(min));
this.min = min;
}
/**
* Gets the minimum date and time in the date time picker. Dates and times
* before that will be disabled in the popups.
*
* @return the minimum date and time that is allowed to be set, or
* null
if there's no minimum
*/
public LocalDateTime getMin() {
return PARSER.apply(getElement().getProperty("min"));
}
/**
* Sets the maximum date and time in the date time picker. Dates and times
* above that will be disabled in the popups.
*
* @param max
* the maximum date and time that is allowed to be set, or
* null
to remove any minimum constraints
*/
public void setMax(LocalDateTime max) {
getElement().setProperty("max", FORMATTER.apply(max));
this.max = max;
}
/**
* Gets the maximum date and time in the date time picker. Dates and times
* above that will be disabled in the popups.
*
* @return the maximum date and time that is allowed to be set, or
* null
if there's no minimum
*/
public LocalDateTime getMax() {
return PARSER.apply(getElement().getProperty("max"));
}
/**
* Gets the internationalization object previously set for this component.
*
* Note: updating the object content that is gotten from this method will
* not update the lang on the component if not set back using
* {@link DateTimePicker#setDatePickerI18n(DatePickerI18n)}
*
* @return the i18n object. It will be null
, If the i18n
* properties weren't set.
*/
public DatePickerI18n getDatePickerI18n() {
return i18n;
}
/**
* Sets the internationalization properties for the date picker inside this
* component.
*
* @param i18n
* the internationalized properties, not null
*/
public void setDatePickerI18n(DatePickerI18n i18n) {
Objects.requireNonNull(i18n,
"The i18n properties object should not be null");
this.i18n = i18n;
datePicker.setI18n(i18n);
}
/**
* Sets whether the date time picker is marked as input required.
*
* @param requiredIndicatorVisible
* the value of the requiredIndicatorVisible to be set
*/
@Override
public void setRequiredIndicatorVisible(boolean requiredIndicatorVisible) {
super.setRequiredIndicatorVisible(requiredIndicatorVisible);
this.required = requiredIndicatorVisible;
}
/**
* When auto open is enabled, the dropdown will open when the field is
* clicked.
*
* @param autoOpen
* Value for the auto open property,
*/
public void setAutoOpen(boolean autoOpen) {
getElement().setProperty("autoOpenDisabled", !autoOpen);
datePicker.setAutoOpen(autoOpen);
timePicker.setAutoOpen(autoOpen);
}
@Override
protected void onAttach(AttachEvent attachEvent) {
super.onAttach(attachEvent);
ClientValidationUtil.preventWebComponentFromModifyingInvalidState(this);
}
}