Skip to content

Commit 567228b

Browse files
committed
feat: add support for DatePickerEx date-based styles
1 parent 9910115 commit 567228b

File tree

3 files changed

+100
-1
lines changed

3 files changed

+100
-1
lines changed

src/main/java/com/flowingcode/addons/ycalendar/DatePickerEx.java

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,16 @@
1919
*/
2020
package com.flowingcode.addons.ycalendar;
2121

22+
import com.vaadin.flow.component.ClientCallable;
2223
import com.vaadin.flow.component.Tag;
2324
import com.vaadin.flow.component.datepicker.DatePicker;
2425
import com.vaadin.flow.component.dependency.JsModule;
2526
import com.vaadin.flow.function.ValueProvider;
27+
import elemental.json.Json;
28+
import elemental.json.JsonObject;
2629
import java.time.LocalDate;
30+
import java.time.YearMonth;
31+
import java.util.Optional;
2732

2833
@SuppressWarnings("serial")
2934
@Tag("fc-date-picker")
@@ -50,4 +55,35 @@ public DatePickerEx(LocalDate initialDate) {
5055
setValue(initialDate);
5156
}
5257

58+
/**
59+
* Sets the function that generates CSS class names for days in this calendar.
60+
*
61+
* Returning {@code null} from the generator results in no custom class name being set. Multiple
62+
* class names can be returned from the generator as space-separated.
63+
*
64+
* @param classNameGenerator the {@code ValueProvider} to use for generating class names
65+
*/
66+
public void setClassNameGenerator(ValueProvider<LocalDate, String> classNameGenerator) {
67+
this.classNameGenerator = classNameGenerator;
68+
refreshAll();
69+
}
70+
71+
/** Refresh the styles of all dates in the displayed year and month. */
72+
public void refreshAll() {
73+
// getElement().executeJs("setTimeout(()=>this._clearEmptyDaysStyle())");
74+
}
75+
76+
@ClientCallable
77+
private JsonObject fetchStyles(String yearMonthStr) {
78+
YearMonth yearMonth = YearMonth.parse(yearMonthStr);
79+
JsonObject monthStyles = Json.createObject();
80+
for (int i = 1, n = yearMonth.lengthOfMonth(); i <= n; i++) {
81+
LocalDate date = yearMonth.atDay(i);
82+
Optional.ofNullable(classNameGenerator).map(g -> g.apply(date)).ifPresent(className -> {
83+
monthStyles.put(Integer.toString(date.getDayOfMonth()), className);
84+
});
85+
}
86+
return monthStyles;
87+
}
88+
5389
}

src/main/resources/META-INF/frontend/fc-date-picker/fc-date-picker.js

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,64 @@ export class FcDatePicker extends DatePicker {
2323
}
2424

2525
ready() {
26+
super.ready();
27+
2628
const buttons=document.createElement('fc-date-picker-buttons');
2729
buttons.setAttribute('slot','prefix');
2830
this.appendChild(buttons);
2931
buttons.addEventListener("click-up", ()=>this._onButtonClick(+1));
3032
buttons.addEventListener("click-down", ()=>this._onButtonClick(-1));
31-
super.ready();
33+
34+
this._styles={};
35+
36+
const _clearStyles = function() {
37+
let e = this.shadowRoot.querySelectorAll("[part~='date']");
38+
e.forEach(item => item.removeAttribute('class'));
39+
};
40+
41+
const _setStyleForDay = function(i,className) {
42+
let e = this.shadowRoot.querySelectorAll("[part~='date']:not(:empty)")[i-1];
43+
if (className) {
44+
e.className=className;
45+
} else {
46+
e.removeAttribute('class');
47+
}
48+
};
49+
50+
const _getStyle = key => {
51+
return this._styles[key] || (this._styles[key] = this.$server.fetchStyles(key));
52+
}
53+
54+
const _updateMonthStyles = function(element) {
55+
const month = element.month;
56+
const key = month.toISOString().substr(0,7);
57+
_clearStyles.call(element);
58+
_getStyle(key).then(styles=>setTimeout(()=>{
59+
if (element.month===month) {
60+
for (let i in styles) {
61+
_setStyleForDay.call(element,i,styles[i]);
62+
}
63+
}
64+
}));
65+
}
66+
67+
this._overlayElement.renderer = e => {
68+
this._boundOverlayRenderer.call(this,e);
69+
70+
if (!this._overlayContent._monthScroller.__fcWrapped) {
71+
const createElement = this._overlayContent._monthScroller._createElement;
72+
this._overlayContent._monthScroller.__fcWrapped = true;
73+
this._overlayContent._monthScroller._createElement = () => {
74+
var calendar = createElement();
75+
calendar.addEventListener('dom-change',ev=>{
76+
if (ev.composedPath()[0].as=='week') {
77+
setTimeout(()=> _updateMonthStyles(calendar));
78+
}
79+
});
80+
return calendar;
81+
}
82+
}
83+
};
3284
}
3385

3486
_onButtonClick(delta) {

src/test/java/com/flowingcode/addons/ycalendar/DatePickerDemo.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import com.vaadin.flow.component.notification.Notification;
2525
import com.vaadin.flow.router.PageTitle;
2626
import com.vaadin.flow.router.Route;
27+
import java.time.DayOfWeek;
2728
import java.time.LocalDate;
2829
import java.util.Objects;
2930

@@ -43,6 +44,16 @@ public DatePickerDemo() {
4344
Notification.show(Objects.toString(ev.getValue()));
4445
});
4546

47+
field.setClassNameGenerator(date -> {
48+
if (date.getDayOfWeek() == DayOfWeek.SATURDAY || date.getDayOfWeek() == DayOfWeek.SUNDAY) {
49+
return "weekend";
50+
} else if (TestUtils.isPublicHoliday(date)) {
51+
return "holiday";
52+
} else {
53+
return null;
54+
}
55+
});
56+
4657
add(field);
4758
}
4859

0 commit comments

Comments
 (0)