getDatePickerI18n()
145 | {
146 | return this.datePickerI18n;
147 | }
148 |
149 | /**
150 | * Sets the locale used for formatting the "expand" button.
151 | * If the locale is null
(default) the clientside locale will be used or {@link Locale#US} if none
152 | * could be detected.
153 | *
154 | */
155 | public DateRangePicker withFormatLocale(final Locale locale)
156 | {
157 | this.useClientSideLocale = locale == null;
158 | this.formatLocale = Optional.ofNullable(locale);
159 | return this;
160 | }
161 |
162 | public Locale getFormatLocale()
163 | {
164 | return this.formatLocale.orElse(DEFAULT_LOCALE);
165 | }
166 |
167 | public DateRangePicker withDateRangeLocalizerFunction(final ItemLabelGenerator dateRangeLocalizerFunction)
168 | {
169 | this.dateRangeLocalizerFunction = dateRangeLocalizerFunction;
170 | return this;
171 | }
172 |
173 | public ItemLabelGenerator getDateRangeLocalizerFunction()
174 | {
175 | return this.dateRangeLocalizerFunction;
176 | }
177 |
178 | /**
179 | * Shortcut for {@link DateRangePicker#setStartLabel(String)}
180 | */
181 | public DateRangePicker withStartLabel(final String label)
182 | {
183 | this.setStartLabel(label);
184 | return this;
185 | }
186 |
187 | /**
188 | * Shortcut for {@link DateRangePicker#setEndLabel(String)}
189 | */
190 | public DateRangePicker withEndLabel(final String label)
191 | {
192 | this.setEndLabel(label);
193 | return this;
194 | }
195 |
196 | /**
197 | * Shortcut for {@link DateRangePicker#setDateRangeOptionsLabel(String)}
198 | */
199 | public DateRangePicker withDateRangeOptionsLabel(final String label)
200 | {
201 | this.setDateRangeOptionsLabel(label);
202 | return this;
203 | }
204 |
205 | /**
206 | * Shortcut for {@link DateRangePicker#setAllowRangeLimitExceeding(boolean)}
207 | */
208 | public DateRangePicker withAllowRangeLimitExceeding(final boolean allowRangeLimitExceeding)
209 | {
210 | this.setAllowRangeLimitExceeding(allowRangeLimitExceeding);
211 | return this;
212 | }
213 |
214 | // endregion
215 |
216 | protected void initUI()
217 | {
218 | // Set an unique ID for each element
219 | this.setId("DateRangePickerID" + nextID.incrementAndGet());
220 |
221 | this.btnOverview.addClassNames(DateRangePickerStyles.BUTTON, DateRangePickerStyles.CLICKABLE);
222 | this.btnOverview.setWidthFull();
223 |
224 | this.btnOverview.setDisableOnClick(true);
225 |
226 | this.overlay.addClassName(DateRangePickerStyles.OVERLAY_LAYOUT);
227 |
228 | this.overlay.setWidthFull();
229 | this.overlay.setHeight("auto");
230 |
231 | this.overlayContainer.setWidthFull();
232 | this.overlayContainer.addClassName(DateRangePickerStyles.OVERLAY_BASE);
233 | this.overlayContainer.add(this.overlay);
234 |
235 | this.getContent().setSpacing(false);
236 | this.getContent().setPadding(false);
237 | this.setSizeUndefined();
238 | this.add(this.btnOverview, this.overlayContainer);
239 |
240 | this.setExpanded(false);
241 | }
242 |
243 | protected void registerListeners()
244 | {
245 | this.btnOverview.addClickListener(ev ->
246 | {
247 | this.toggle();
248 | ev.getSource().setEnabled(true);
249 | });
250 | this.overlay.addValueChangeListener(ev ->
251 | {
252 | this.model = ev.getSource().getModel();
253 |
254 | this.updateFromModel(false);
255 | this.fireEvent(new DateRangeValueChangeEvent<>(this, ev.getOldValue(), ev.isFromClient()));
256 | });
257 | }
258 |
259 | @Override
260 | protected void onAttach(final AttachEvent attachEvent)
261 | {
262 | this.setLocaleFromClient();
263 |
264 | this.updateFromModel(true);
265 |
266 | this.addClickOutsideListener();
267 | }
268 |
269 | protected void setLocaleFromClient()
270 | {
271 | if(this.useClientSideLocale)
272 | {
273 | this.formatLocale = Optional.ofNullable(VaadinService.getCurrentRequest().getLocale());
274 | }
275 | }
276 |
277 | protected void addClickOutsideListener()
278 | {
279 | if(!this.isCloseOnOutsideClick())
280 | {
281 | return;
282 | }
283 |
284 | final String funcName = "outsideClickFunc" + this.getId().orElseThrow();
285 |
286 | final String jsCommand = String.join(
287 | "\r\n",
288 | // Define Click-Function
289 | "var " + funcName + " = function(event) {",
290 | // Get the current Element
291 | " var spEl = document.getElementById('" + this.getId().orElseThrow() + "');",
292 | " if (!spEl) {",
293 | // If the element got detached/removed, then als delete the listener of the base element
294 | " document.removeEventListener('click'," + funcName + ");",
295 | " return;",
296 | " }",
297 | // Check if a Vaadin overlay caused the click
298 | " let parent = event.target;",
299 | // Check all parents of clicked element
300 | " while(parent) {",
301 | // Check if a vaadin overlay was clicked:
302 | // Fist check if the tagName indicates a Vaadin overlay
303 | // If not fallback to id='overlay'
304 | " let tagName = parent.tagName.toLowerCase();",
305 | " if((tagName.includes('vaadin') && tagName.includes('overlay')) || parent.id == 'overlay') {",
306 | " return;",
307 | " }",
308 | " parent = parent.parentElement;",
309 | " }",
310 | // Check if the click was done on this element
311 | " var isClickInside = spEl.contains(event.target);",
312 | " if (!isClickInside) {",
313 | " spEl.$server.clickOutsideOccurred();",
314 | " }",
315 | "}; ",
316 | "document.body.addEventListener('click'," + funcName + ");"
317 | );
318 |
319 | this.getContent().getElement().executeJs(jsCommand);
320 | }
321 |
322 | @ClientCallable
323 | protected void clickOutsideOccurred()
324 | {
325 | if(!this.isCloseOnOutsideClick())
326 | {
327 | return;
328 | }
329 |
330 | if(this.isExpanded())
331 | {
332 | this.setExpanded(false);
333 | }
334 | }
335 |
336 | protected void updateFromModel(final boolean updateOverlay)
337 | {
338 | if(updateOverlay)
339 | {
340 | this.tryFixInvalidModel();
341 | }
342 |
343 | final DateTimeFormatter formatter =
344 | DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT).withLocale(this.getFormatLocale());
345 |
346 | this.btnOverview.setText(this.model.getStart().format(formatter)
347 | + (this.model.getStart().equals(this.model.getEnd()) ? "" : " - " + this.model.getEnd().format(formatter))
348 | );
349 |
350 | if(updateOverlay)
351 | {
352 | this.overlay.setModel(this.model);
353 | }
354 | }
355 |
356 | protected void tryFixInvalidModel()
357 | {
358 | this.model.getDateRange()
359 | .calcFor(this.model.getStart())
360 | .ifPresent(result -> {
361 | this.model.setStart(result.getStart());
362 | this.model.setEnd(result.getEnd());
363 | });
364 | }
365 |
366 | protected void toggle()
367 | {
368 | this.setExpanded(!this.isExpanded());
369 | }
370 |
371 | protected synchronized void setExpanded(final boolean expanded)
372 | {
373 | this.expanded = expanded;
374 | this.btnOverview.setIcon(expanded ? VaadinIcon.CARET_DOWN.create() : VaadinIcon.CARET_UP.create());
375 |
376 | this.overlay.setVisible(expanded);
377 | }
378 |
379 | public synchronized boolean isExpanded()
380 | {
381 | return this.expanded;
382 | }
383 |
384 | // region Get UI elements
385 |
386 | public DateRangePickerOverlay getOverlay()
387 | {
388 | return this.overlay;
389 | }
390 |
391 | public Button getBtnOverview()
392 | {
393 | return this.btnOverview;
394 | }
395 |
396 | public Div getOverlayContainer()
397 | {
398 | return this.overlayContainer;
399 | }
400 |
401 | // endregion
402 |
403 | // region Labels
404 |
405 | /**
406 | * Sets the label for the overlay Start-DatePicker
407 | */
408 | public void setStartLabel(final String label)
409 | {
410 | Objects.requireNonNull(label);
411 | this.getOverlay().getDpStart().setLabel(label);
412 | }
413 |
414 | /**
415 | * Sets the label for the overlay End-DatePicker
416 | */
417 | public void setEndLabel(final String label)
418 | {
419 | Objects.requireNonNull(label);
420 | this.getOverlay().getDpEnd().setLabel(label);
421 | }
422 |
423 | /**
424 | * Sets the label for the overlay DateRange-ComboBox
425 | */
426 | public void setDateRangeOptionsLabel(final String label)
427 | {
428 | Objects.requireNonNull(label);
429 | this.getOverlay().getCbDateRange().setLabel(label);
430 | }
431 |
432 | // endregion
433 |
434 | // region AllowRangeLimitExceeding
435 |
436 | /**
437 | * Allows the maximum start and end date to be greater or less than the configured end or start date.
438 | *
439 | * This is only the case when {@link DateRange#isCalcable()} is true
. Otherwise incorrect values (e.g.
440 | * start before end) could be set.
441 | */
442 | public void setAllowRangeLimitExceeding(final boolean allowRangeLimitExceeding)
443 | {
444 | this.allowRangeLimitExceeding = allowRangeLimitExceeding;
445 | }
446 |
447 | public boolean isAllowRangeLimitExceeding()
448 | {
449 | return this.allowRangeLimitExceeding;
450 | }
451 |
452 | // endregion
453 |
454 | // region Data
455 |
456 | /**
457 | * Uses the given {@link DateRange} and calculates with the current Date the {@link DateRangeModel}, which is then
458 | * set by {@link DateRangePicker#setValue(DateRangeModel)}
459 | */
460 | public void setDateRangeForToday(final D range)
461 | {
462 | range.calcFor(LocalDate.now()).ifPresent(
463 | result -> this.setValue(new DateRangeModel<>(result.getStart(), result.getEnd(), range)));
464 | }
465 |
466 | @Override
467 | public void setItems(final Collection items)
468 | {
469 | this.overlay.setItems(items);
470 | }
471 |
472 | @Override
473 | public LocalDate getStart()
474 | {
475 | return this.model.getStart();
476 | }
477 |
478 | @Override
479 | public DateRangePicker setStart(final LocalDate start)
480 | {
481 | this.model.setStart(start);
482 | this.updateFromModel(true);
483 | return this;
484 | }
485 |
486 | @Override
487 | public LocalDate getEnd()
488 | {
489 | return this.model.getEnd();
490 | }
491 |
492 | @Override
493 | public DateRangePicker setEnd(final LocalDate end)
494 | {
495 | this.model.setEnd(end);
496 | this.updateFromModel(true);
497 | return this;
498 | }
499 |
500 | @Override
501 | public D getDateRange()
502 | {
503 | return this.model.getDateRange();
504 | }
505 |
506 | @Override
507 | public DateRangePicker setDateRange(final D dateRange)
508 | {
509 | this.model.setDateRange(dateRange);
510 | this.updateFromModel(true);
511 | return this;
512 | }
513 |
514 | @Override
515 | public void setValue(final DateRangeModel value)
516 | {
517 | Objects.requireNonNull(value);
518 |
519 | this.model = value;
520 | this.updateFromModel(true);
521 | }
522 |
523 | @Override
524 | public DateRangeModel getValue()
525 | {
526 | return this.model;
527 | }
528 |
529 | @SuppressWarnings("unchecked")
530 | @Override
531 | public Registration addValueChangeListener(final ValueChangeListener super DateRangeValueChangeEvent> listener)
532 | {
533 | @SuppressWarnings("rawtypes")
534 | final ComponentEventListener componentListener =
535 | event -> listener.valueChanged((DateRangeValueChangeEvent)event);
536 |
537 | return ComponentUtil.addListener(this, DateRangeValueChangeEvent.class, componentListener);
538 | }
539 |
540 | /**
541 | * DateRangePicker always has a value
542 | * However for compatibility reasons (with Vaadin) this returns {@code null}
543 | * @return {@code null}
544 | */
545 | @Override
546 | public DateRangeModel getEmptyValue()
547 | {
548 | return null;
549 | }
550 |
551 | /**
552 | * DateRangePicker always has a value
553 | * Therefore this always returns {@code false}
554 | *
555 | * @return {@code false}
556 | */
557 | @Override
558 | public boolean isEmpty()
559 | {
560 | return false;
561 | }
562 |
563 | /**
564 | * Do not use this method, as it throws a {@link UnsupportedOperationException}
565 | * The calling of clear is not supported because DateRangePicker always has a value
566 | * Use {@link DateRangePicker#setValue(DateRangeModel)} instead.
567 | *
568 | * @throws UnsupportedOperationException DateRangePicker always has a value
569 | */
570 | @Override
571 | public void clear()
572 | {
573 | throw new UnsupportedOperationException(
574 | "The calling of clear is not supported because DateRangePicker always has a value");
575 | }
576 |
577 | @Override
578 | public void setReadOnly(final boolean readOnly)
579 | {
580 | this.getOverlay().setReadOnly(readOnly);
581 | }
582 |
583 | @Override
584 | public boolean isReadOnly()
585 | {
586 | return this.getOverlay().isReadOnly();
587 | }
588 |
589 | /**
590 | * The required indicator is not implemented
591 | *
592 | * This method doesn't have any functionallity
593 | */
594 | @Override
595 | public void setRequiredIndicatorVisible(final boolean requiredIndicatorVisible)
596 | {
597 | // Not required/implemented
598 | }
599 |
600 | /**
601 | * The required indicator is not implemented
This will always return {@code false}
602 | *
603 | * @return {@code false}
604 | */
605 | @Override
606 | public boolean isRequiredIndicatorVisible()
607 | {
608 | return false;
609 | }
610 |
611 | // endregion
612 | }
613 |
--------------------------------------------------------------------------------
/vaadin-date-range-picker/src/main/java/software/xdev/vaadin/daterange_picker/ui/DateRangePickerOverlay.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © 2020 XDEV Software (https://xdev.software)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package software.xdev.vaadin.daterange_picker.ui;
17 |
18 | import java.time.LocalDate;
19 | import java.util.Collection;
20 | import java.util.Objects;
21 | import java.util.Optional;
22 | import java.util.function.Function;
23 |
24 | import com.vaadin.flow.component.AbstractField.ComponentValueChangeEvent;
25 | import com.vaadin.flow.component.AttachEvent;
26 | import com.vaadin.flow.component.ComponentEvent;
27 | import com.vaadin.flow.component.ComponentEventListener;
28 | import com.vaadin.flow.component.Composite;
29 | import com.vaadin.flow.component.HasStyle;
30 | import com.vaadin.flow.component.button.Button;
31 | import com.vaadin.flow.component.combobox.ComboBox;
32 | import com.vaadin.flow.component.datepicker.DatePicker;
33 | import com.vaadin.flow.component.dependency.CssImport;
34 | import com.vaadin.flow.component.icon.VaadinIcon;
35 | import com.vaadin.flow.component.orderedlayout.FlexComponent;
36 | import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
37 | import com.vaadin.flow.component.orderedlayout.VerticalLayout;
38 | import com.vaadin.flow.data.binder.HasItems;
39 | import com.vaadin.flow.shared.Registration;
40 |
41 | import software.xdev.vaadin.daterange_picker.business.DateRange;
42 | import software.xdev.vaadin.daterange_picker.business.DateRangeModel;
43 | import software.xdev.vaadin.daterange_picker.business.DateRangeResult;
44 |
45 |
46 | /**
47 | * Overlay of the expanded {@link DateRangePicker}
48 | */
49 | @SuppressWarnings("java:S1948")
50 | @CssImport(DateRangePickerStyles.LOCATION)
51 | public class DateRangePickerOverlay extends Composite implements
52 | HasItems,
53 | FlexComponent
54 | {
55 | /*
56 | * Fields
57 | */
58 | protected boolean readOnly;
59 |
60 | protected DateRangePicker dateRangePicker;
61 | protected DateRangeModel currentModel;
62 |
63 | /*
64 | * UI-Comp
65 | */
66 | protected final Button btnBackwardRange = new Button(VaadinIcon.ANGLE_LEFT.create());
67 | protected ComboBox cbDateRange = new ComboBox<>("Period");
68 | protected final Button btnForwardRange = new Button(VaadinIcon.ANGLE_RIGHT.create());
69 |
70 | protected DatePicker dpStart = new DatePicker("Start");
71 | protected DatePicker dpEnd = new DatePicker("End");
72 |
73 | public DateRangePickerOverlay(final DateRangePicker dateRangePicker)
74 | {
75 | this.dateRangePicker = Objects.requireNonNull(dateRangePicker);
76 | this.currentModel = this.dateRangePicker.getValue();
77 |
78 | this.initUI();
79 | this.registerListeners();
80 | }
81 |
82 | protected void initUI()
83 | {
84 | this.btnBackwardRange
85 | .addClassNames(DateRangePickerStyles.FLEX_CHILD_CONTENTSIZE, DateRangePickerStyles.CLICKABLE);
86 |
87 | this.cbDateRange.addClassNames(
88 | DateRangePickerStyles.FLEX_CHILD_AUTOGROW,
89 | DateRangePickerStyles.PADDING_TOP_XS);
90 | this.setTextFieldDefaultWidthFlexConform(this.cbDateRange);
91 |
92 | this.btnForwardRange
93 | .addClassNames(DateRangePickerStyles.FLEX_CHILD_CONTENTSIZE, DateRangePickerStyles.CLICKABLE);
94 |
95 | final HorizontalLayout hlRange = new HorizontalLayout();
96 | hlRange.addClassNames(DateRangePickerStyles.OVERLAY_LAYOUT_ROW);
97 | hlRange.setAlignItems(Alignment.BASELINE);
98 | hlRange.setJustifyContentMode(JustifyContentMode.BETWEEN);
99 | hlRange.setMargin(false);
100 | hlRange.setSpacing(true);
101 | hlRange.setPadding(false);
102 | hlRange.add(this.btnBackwardRange, this.cbDateRange, this.btnForwardRange);
103 |
104 | this.initDatePicker(this.dpStart);
105 | this.initDatePicker(this.dpEnd);
106 |
107 | final HorizontalLayout hlDatepickers = new HorizontalLayout();
108 | hlDatepickers.addClassNames(DateRangePickerStyles.OVERLAY_LAYOUT_ROW);
109 | hlDatepickers.setMargin(false);
110 | hlDatepickers.setSpacing(true);
111 | hlDatepickers.setPadding(false);
112 | hlDatepickers.add(this.dpStart, this.dpEnd);
113 |
114 | this.add(hlRange, hlDatepickers);
115 | this.getContent().setPadding(true);
116 | }
117 |
118 | protected void initDatePicker(final DatePicker dp)
119 | {
120 | this.setTextFieldDefaultWidthFlexConform(dp);
121 | dp.addClassNames(DateRangePickerStyles.FLEX_CHILD_AUTOGROW, DateRangePickerStyles.PADDING_TOP_XS);
122 | dp.setWeekNumbersVisible(true);
123 | }
124 |
125 | @Override
126 | protected void onAttach(final AttachEvent attachEvent)
127 | {
128 | this.cbDateRange.setItemLabelGenerator(this.dateRangePicker.getDateRangeLocalizerFunction());
129 |
130 | this.dateRangePicker.getDatePickerI18n()
131 | .ifPresent(i18n ->
132 | {
133 | this.dpStart.setI18n(i18n);
134 | this.dpEnd.setI18n(i18n);
135 | });
136 | }
137 |
138 | protected void setTextFieldDefaultWidthFlexConform(final HasStyle component)
139 | {
140 | component.getStyle().set("--vaadin-field-default-width", "auto");
141 | }
142 |
143 | protected void registerListeners()
144 | {
145 | this.cbDateRange.addValueChangeListener(this::onComboBoxDateRangeValueChanged);
146 | this.btnBackwardRange.addClickListener(ev -> this.moveRange(-1));
147 | this.btnForwardRange.addClickListener(ev -> this.moveRange(+1));
148 | this.dpStart.addValueChangeListener(this::onDatePickerValueChanged);
149 | this.dpEnd.addValueChangeListener(this::onDatePickerValueChanged);
150 | }
151 |
152 | protected void onComboBoxDateRangeValueChanged(final ComponentValueChangeEvent, D> ev)
153 | {
154 | if(!ev.isFromClient())
155 | {
156 | return;
157 | }
158 | this.onValueChange(model -> model.getDateRange().calcFor(model.getStart()));
159 | }
160 |
161 | protected void onDatePickerValueChanged(final ComponentValueChangeEvent ev)
162 | {
163 | if(!ev.isFromClient())
164 | {
165 | return;
166 | }
167 | this.onValueChange(model -> model.getDateRange().calcFor(ev.getValue()));
168 | }
169 |
170 | protected void moveRange(final int dif)
171 | {
172 | this.onValueChange(model -> model.getDateRange().moveDateRange(model.getStart(), dif));
173 | }
174 |
175 | protected void calcModel(final Optional optResult, final DateRangeModel model)
176 | {
177 | if(optResult.isEmpty())
178 | {
179 | return;
180 | }
181 |
182 | final DateRangeResult result = optResult.get();
183 | model.setStart(result.getStart());
184 | model.setEnd(result.getEnd());
185 | }
186 |
187 | protected void onValueChange(final Function, Optional> calcFunc)
188 | {
189 | final DateRangeModel model = this.getModelFromComponents();
190 |
191 | this.calcModel(calcFunc.apply(model), model);
192 | this.updateComponentsFromModel(model);
193 |
194 | final DateRangeModel oldValue = this.currentModel;
195 | this.setCurrentModel(model);
196 |
197 | this.fireValueChanged(oldValue, true);
198 | }
199 |
200 | protected DateRangeModel getModelFromComponents()
201 | {
202 | return new DateRangeModel<>(this.dpStart.getValue(), this.dpEnd.getValue(), this.cbDateRange.getValue());
203 | }
204 |
205 | protected void updateComponentsFromModel(final DateRangeModel model)
206 | {
207 | final boolean datepickerReadonly = !model.getDateRange().isSettable();
208 | this.dpStart.setReadOnly(datepickerReadonly);
209 | this.dpEnd.setReadOnly(datepickerReadonly);
210 |
211 | final boolean fastNavEnabled = model.getDateRange().isMovable();
212 | this.btnBackwardRange.setEnabled(fastNavEnabled);
213 | this.btnForwardRange.setEnabled(fastNavEnabled);
214 |
215 | final boolean allowRangeLimitExceeding =
216 | this.dateRangePicker.isAllowRangeLimitExceeding()
217 | // If it's not calcable we can't verify that the set value is correct (e.g. when it's a free value)
218 | && model.getDateRange().isCalcable();
219 | this.dpEnd.setMin(allowRangeLimitExceeding ? null : model.getStart());
220 | this.dpStart.setMax(allowRangeLimitExceeding ? null : model.getEnd());
221 |
222 | this.cbDateRange.setValue(model.getDateRange());
223 | this.dpStart.setValue(model.getStart());
224 | this.dpEnd.setValue(model.getEnd());
225 | }
226 |
227 | protected void setCurrentModel(final DateRangeModel model)
228 | {
229 | this.currentModel = model;
230 | }
231 |
232 | protected void fireValueChanged(final DateRangeModel oldValue, final boolean isFromClient)
233 | {
234 | this.fireEvent(new DateRangeOverlayValueChangeEvent(this, oldValue, isFromClient));
235 | }
236 |
237 | @Override
238 | public void setItems(final Collection items)
239 | {
240 | Objects.requireNonNull(items);
241 |
242 | this.getCbDateRange().setItems(items);
243 | }
244 |
245 | // region UI Getter
246 | public ComboBox getCbDateRange()
247 | {
248 | return this.cbDateRange;
249 | }
250 |
251 | public DatePicker getDpStart()
252 | {
253 | return this.dpStart;
254 | }
255 |
256 | public DatePicker getDpEnd()
257 | {
258 | return this.dpEnd;
259 | }
260 |
261 | // endregion
262 |
263 | // region Manage Data externally
264 | public DateRangeModel getModel()
265 | {
266 | return this.currentModel;
267 | }
268 |
269 | public void setModel(final DateRangeModel model)
270 | {
271 | final DateRangeModel oldValue = this.currentModel;
272 |
273 | this.currentModel = model;
274 | this.updateComponentsFromModel(this.currentModel);
275 |
276 | this.fireValueChanged(oldValue, false);
277 | }
278 |
279 | public void setReadOnly(final boolean readOnly)
280 | {
281 | this.readOnly = readOnly;
282 |
283 | this.cbDateRange.setReadOnly(readOnly);
284 |
285 | this.btnBackwardRange.setEnabled(!readOnly);
286 | this.btnForwardRange.setEnabled(!readOnly);
287 |
288 | if(readOnly)
289 | {
290 | this.dpStart.setReadOnly(true);
291 | this.dpEnd.setReadOnly(true);
292 | }
293 | else
294 | {
295 | // Fix read-only if e.g. TODAY is selected
296 | this.updateComponentsFromModel(this.getModelFromComponents());
297 | }
298 | }
299 |
300 | public boolean isReadOnly()
301 | {
302 | return this.readOnly;
303 | }
304 |
305 | @SuppressWarnings({"unchecked", "rawtypes"})
306 | public Registration addValueChangeListener(final ComponentEventListener listener)
307 | {
308 | return this.addListener(DateRangeOverlayValueChangeEvent.class, (ComponentEventListener)listener);
309 | }
310 |
311 | // endregion
312 |
313 | public class DateRangeOverlayValueChangeEvent extends ComponentEvent>
314 | {
315 | private final DateRangeModel oldValue;
316 | private final boolean isFromClient;
317 |
318 | public DateRangeOverlayValueChangeEvent(
319 | final DateRangePickerOverlay source,
320 | final DateRangeModel oldValue,
321 | final boolean isFromClient)
322 | {
323 | super(source, false);
324 | this.oldValue = oldValue;
325 | this.isFromClient = isFromClient;
326 | }
327 |
328 | public DateRangeModel getOldValue()
329 | {
330 | return this.oldValue;
331 | }
332 |
333 | @Override
334 | public boolean isFromClient()
335 | {
336 | return this.isFromClient;
337 | }
338 | }
339 | }
340 |
--------------------------------------------------------------------------------
/vaadin-date-range-picker/src/main/java/software/xdev/vaadin/daterange_picker/ui/DateRangePickerStyles.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © 2020 XDEV Software (https://xdev.software)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package software.xdev.vaadin.daterange_picker.ui;
17 |
18 | /**
19 | * Styles for the {@link DateRangePicker}
20 | */
21 | public final class DateRangePickerStyles
22 | {
23 | private DateRangePickerStyles()
24 | {
25 | }
26 |
27 | public static final String LOCATION = "./styles/dateRangePicker.css";
28 |
29 | public static final String CLICKABLE = "date-range-picker-clickable";
30 |
31 | public static final String BUTTON = "date-range-picker-button";
32 | public static final String OVERLAY_BASE = "date-range-picker-overlay-base";
33 | public static final String OVERLAY_LAYOUT = "date-range-picker-overlay-layout";
34 | public static final String OVERLAY_LAYOUT_ROW = "date-range-picker-overlay-layout-row";
35 |
36 | /*
37 | * FLEX
38 | */
39 | public static final String FLEX_CHILD_AUTOGROW = "date-range-picker-flex-child-autogrow";
40 | public static final String FLEX_CHILD_CONTENTSIZE = "date-range-picker-flex-child-contentsize";
41 |
42 | // Used to remove Vaadin's default padding which adds a lot of blank space to the overlay
43 | public static final String PADDING_TOP_XS = "date-range-picker-padding-top-xs";
44 | }
45 |
--------------------------------------------------------------------------------
/vaadin-date-range-picker/src/main/java/software/xdev/vaadin/daterange_picker/ui/DateRangeValueChangeEvent.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © 2020 XDEV Software (https://xdev.software)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package software.xdev.vaadin.daterange_picker.ui;
17 |
18 | import com.vaadin.flow.component.AbstractField.ComponentValueChangeEvent;
19 |
20 | import software.xdev.vaadin.daterange_picker.business.DateRange;
21 | import software.xdev.vaadin.daterange_picker.business.DateRangeModel;
22 |
23 |
24 | public class DateRangeValueChangeEvent
25 | extends ComponentValueChangeEvent, DateRangeModel>
26 | {
27 | public DateRangeValueChangeEvent(
28 | final DateRangePicker source,
29 | final DateRangeModel oldValue,
30 | final boolean isFromClient)
31 | {
32 | super(source, source, oldValue, isFromClient);
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/vaadin-date-range-picker/src/main/resources/META-INF/resources/frontend/styles/dateRangePicker.css:
--------------------------------------------------------------------------------
1 | .date-range-picker-clickable {
2 | cursor: pointer;
3 | }
4 |
5 | .date-range-picker-button {
6 | margin-bottom: 0;
7 | border-radius: 0;
8 | color: var(--lumo-body-text-color);
9 | min-width: var(--date-range-picker-min-width, 20em);
10 | }
11 |
12 | .date-range-picker-overlay-base {
13 | position: relative;
14 | }
15 |
16 | .date-range-picker-overlay-layout {
17 | display: flex;
18 | position: absolute;
19 | left: 0;
20 | right: 0;
21 | z-index: 1;
22 | background-color: var(--lumo-base-color);
23 | border: 1px solid var(--lumo-contrast-5pct);
24 | border-top: none;
25 | min-width: var(--date-range-picker-min-width, 20em);
26 | }
27 |
28 | .date-range-picker-overlay-layout-row {
29 | width: 100%;
30 | flex: 1 1 auto;
31 | display: flex;
32 | }
33 |
34 | .date-range-picker-flex-child-autogrow {
35 | flex: 1 1 auto;
36 | }
37 |
38 | .date-range-picker-flex-child-contentsize {
39 | flex: 0 1 auto;
40 | }
41 |
42 | .date-range-picker-padding-top-xs {
43 | padding-top: var(--lumo-space-xs);
44 | }
45 |
--------------------------------------------------------------------------------