All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
software.xdev.vaadin.daterange_picker.ui.DateRangePickerOverlay Maven / Gradle / Ivy
/*
* Copyright © 2020 XDEV Software (https://xdev.software)
*
* 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 software.xdev.vaadin.daterange_picker.ui;
import java.time.LocalDate;
import java.util.Collection;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Function;
import com.vaadin.flow.component.AbstractField.ComponentValueChangeEvent;
import com.vaadin.flow.component.AttachEvent;
import com.vaadin.flow.component.ComponentEvent;
import com.vaadin.flow.component.ComponentEventListener;
import com.vaadin.flow.component.Composite;
import com.vaadin.flow.component.HasStyle;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.combobox.ComboBox;
import com.vaadin.flow.component.datepicker.DatePicker;
import com.vaadin.flow.component.dependency.CssImport;
import com.vaadin.flow.component.icon.VaadinIcon;
import com.vaadin.flow.component.orderedlayout.FlexComponent;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.data.binder.HasItems;
import com.vaadin.flow.shared.Registration;
import software.xdev.vaadin.daterange_picker.business.DateRange;
import software.xdev.vaadin.daterange_picker.business.DateRangeModel;
import software.xdev.vaadin.daterange_picker.business.DateRangeResult;
/**
* Overlay of the expanded {@link DateRangePicker}
*
* @author AB
*
*/
@CssImport(DateRangePickerStyles.LOCATION)
public class DateRangePickerOverlay extends Composite implements
HasItems,
FlexComponent
{
/*
* Fields
*/
protected boolean readOnly = false;
protected DateRangePicker dateRangePicker;
protected DateRangeModel currentModel;
/*
* UI-Comp
*/
protected final Button btnBackwardRange = new Button(VaadinIcon.ANGLE_LEFT.create());
protected ComboBox cbDateRange = new ComboBox<>("Period");
protected final Button btnForwardRange = new Button(VaadinIcon.ANGLE_RIGHT.create());
protected DatePicker dpStart = new DatePicker("Start");
protected DatePicker dpEnd = new DatePicker("End");
public DateRangePickerOverlay(final DateRangePicker dateRangePicker)
{
this.dateRangePicker = Objects.requireNonNull(dateRangePicker);
this.currentModel = this.dateRangePicker.getValue();
this.initUI();
this.registerListeners();
}
protected void initUI()
{
this.btnBackwardRange
.addClassNames(DateRangePickerStyles.FLEX_CHILD_CONTENTSIZE, DateRangePickerStyles.CLICKABLE);
this.cbDateRange.addClassName(DateRangePickerStyles.FLEX_CHILD_AUTOGROW);
this.setTextFieldDefaultWidthFlexConform(this.cbDateRange);
this.btnForwardRange
.addClassNames(DateRangePickerStyles.FLEX_CHILD_CONTENTSIZE, DateRangePickerStyles.CLICKABLE);
final HorizontalLayout hlRange = new HorizontalLayout();
hlRange.addClassNames(DateRangePickerStyles.FLEX_CHILD_AUTOGROW, DateRangePickerStyles.FLEX_CONTAINER);
hlRange.setAlignItems(Alignment.BASELINE);
hlRange.setJustifyContentMode(JustifyContentMode.BETWEEN);
hlRange.setMargin(false);
hlRange.setSpacing(true);
hlRange.setPadding(false);
hlRange.add(this.btnBackwardRange, this.cbDateRange, this.btnForwardRange);
this.initDatePicker(this.dpStart);
this.initDatePicker(this.dpEnd);
final HorizontalLayout hlDatepickers = new HorizontalLayout();
hlDatepickers.addClassNames(DateRangePickerStyles.FLEX_CHILD_AUTOGROW, DateRangePickerStyles.FLEX_CONTAINER);
hlDatepickers.setMargin(false);
hlDatepickers.setSpacing(true);
hlDatepickers.setPadding(false);
hlDatepickers.add(this.dpStart, this.dpEnd);
this.addClassName(DateRangePickerStyles.FLEX_CONTAINER);
this.add(hlRange, hlDatepickers);
this.getContent().setPadding(true);
}
protected void initDatePicker(final DatePicker dp)
{
this.setTextFieldDefaultWidthFlexConform(dp);
dp.addClassName(DateRangePickerStyles.FLEX_CHILD_AUTOGROW);
dp.setWeekNumbersVisible(true);
}
@Override
protected void onAttach(final AttachEvent attachEvent)
{
this.cbDateRange.setItemLabelGenerator(this.dateRangePicker.getDateRangeLocalizerFunction());
this.dateRangePicker.getDatePickerI18n()
.ifPresent(i18n ->
{
this.dpStart.setI18n(i18n);
this.dpEnd.setI18n(i18n);
});
}
protected void setTextFieldDefaultWidthFlexConform(final HasStyle component)
{
component.getStyle().set("--vaadin-field-default-width", "auto");
}
protected void registerListeners()
{
this.cbDateRange.addValueChangeListener(this::onComboBoxDateRangeValueChanged);
this.btnBackwardRange.addClickListener(ev -> this.moveRange(-1));
this.btnForwardRange.addClickListener(ev -> this.moveRange(+1));
this.dpStart.addValueChangeListener(this::onDatePickerValueChanged);
this.dpEnd.addValueChangeListener(this::onDatePickerValueChanged);
}
protected void onComboBoxDateRangeValueChanged(final ComponentValueChangeEvent, D> ev)
{
if(!ev.isFromClient())
{
return;
}
this.onValueChange(model -> model.getDateRange().calcFor(model.getStart()));
}
protected void onDatePickerValueChanged(final ComponentValueChangeEvent ev)
{
if(!ev.isFromClient())
{
return;
}
this.onValueChange(model -> model.getDateRange().calcFor(ev.getValue()));
}
protected void moveRange(final int dif)
{
this.onValueChange(model -> model.getDateRange().moveDateRange(model.getStart(), dif));
}
protected void calcModel(final Optional optResult, final DateRangeModel model)
{
if(optResult.isEmpty())
{
return;
}
final DateRangeResult result = optResult.get();
model.setStart(result.getStart());
model.setEnd(result.getEnd());
}
protected void onValueChange(final Function, Optional> calcFunc)
{
final DateRangeModel model = this.getModelFromComponents();
this.calcModel(calcFunc.apply(model), model);
this.updateComponentsFromModel(model);
final DateRangeModel oldValue = this.currentModel;
this.setCurrentModel(model);
this.fireValueChanged(oldValue, true);
}
protected DateRangeModel getModelFromComponents()
{
return new DateRangeModel<>(this.dpStart.getValue(), this.dpEnd.getValue(), this.cbDateRange.getValue());
}
protected void updateComponentsFromModel(final DateRangeModel model)
{
final boolean datepickerReadonly = !model.getDateRange().isSettable();
this.dpStart.setReadOnly(datepickerReadonly);
this.dpEnd.setReadOnly(datepickerReadonly);
final boolean fastNavEnabled = model.getDateRange().isMovable();
this.btnBackwardRange.setEnabled(fastNavEnabled);
this.btnForwardRange.setEnabled(fastNavEnabled);
final boolean allowRangeLimitExceeding =
this.dateRangePicker.isAllowRangeLimitExceeding()
// If it's not calcable we can't verify that the set value is correct (e.g. when it's a free value)
&& model.getDateRange().isCalcable();
this.dpEnd.setMin(allowRangeLimitExceeding ? null : model.getStart());
this.dpStart.setMax(allowRangeLimitExceeding ? null : model.getEnd());
this.cbDateRange.setValue(model.getDateRange());
this.dpStart.setValue(model.getStart());
this.dpEnd.setValue(model.getEnd());
}
protected void setCurrentModel(final DateRangeModel model)
{
this.currentModel = model;
}
protected void fireValueChanged(final DateRangeModel oldValue, final boolean isFromClient)
{
this.fireEvent(new DateRangeOverlayValueChangeEvent(this, oldValue, isFromClient));
}
@Override
public void setItems(final Collection items)
{
Objects.requireNonNull(items);
this.getCbDateRange().setItems(items);
}
// region UI Getter
public ComboBox getCbDateRange()
{
return this.cbDateRange;
}
public DatePicker getDpStart()
{
return this.dpStart;
}
public DatePicker getDpEnd()
{
return this.dpEnd;
}
// endregion
// region Manage Data externally
public DateRangeModel getModel()
{
return this.currentModel;
}
public void setModel(final DateRangeModel model)
{
final DateRangeModel oldValue = this.currentModel;
this.currentModel = model;
this.updateComponentsFromModel(this.currentModel);
this.fireValueChanged(oldValue, false);
}
public void setReadOnly(final boolean readOnly)
{
this.readOnly = readOnly;
this.cbDateRange.setReadOnly(readOnly);
this.btnBackwardRange.setEnabled(!readOnly);
this.btnForwardRange.setEnabled(!readOnly);
if(readOnly)
{
this.dpStart.setReadOnly(true);
this.dpEnd.setReadOnly(true);
}
else
{
// Fix read-only if e.g. TODAY is selected
this.updateComponentsFromModel(this.getModelFromComponents());
}
}
public boolean isReadOnly()
{
return this.readOnly;
}
@SuppressWarnings({"unchecked", "rawtypes"})
public Registration addValueChangeListener(final ComponentEventListener listener)
{
return this.addListener(DateRangeOverlayValueChangeEvent.class, (ComponentEventListener)listener);
}
// endregion
public class DateRangeOverlayValueChangeEvent extends ComponentEvent>
{
private final DateRangeModel oldValue;
private final boolean isFromClient;
public DateRangeOverlayValueChangeEvent(
final DateRangePickerOverlay source,
final DateRangeModel oldValue,
final boolean isFromClient)
{
super(source, false);
this.oldValue = oldValue;
this.isFromClient = isFromClient;
}
public DateRangeModel getOldValue()
{
return this.oldValue;
}
@Override
public boolean isFromClient()
{
return this.isFromClient;
}
}
}