Skip to content

Commit cb2af4a

Browse files
javier-godoypaodb
authored andcommitted
feat: add DatePickerEx component
1 parent 6908bcb commit cb2af4a

File tree

5 files changed

+249
-0
lines changed

5 files changed

+249
-0
lines changed
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/*-
2+
* #%L
3+
* Year Month Calendar Add-on
4+
* %%
5+
* Copyright (C) 2021 - 2025 Flowing Code
6+
* %%
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
* #L%
19+
*/
20+
package com.flowingcode.addons.ycalendar;
21+
22+
import com.vaadin.flow.component.Tag;
23+
import com.vaadin.flow.component.datepicker.DatePicker;
24+
import com.vaadin.flow.component.dependency.JsModule;
25+
import com.vaadin.flow.function.ValueProvider;
26+
import java.time.LocalDate;
27+
28+
@SuppressWarnings("serial")
29+
@Tag("fc-date-picker")
30+
@JsModule("./fc-date-picker/fc-date-picker.js")
31+
public class DatePickerEx extends DatePicker {
32+
33+
private ValueProvider<LocalDate, String> classNameGenerator;
34+
35+
/**
36+
* Default constructor.
37+
*/
38+
public DatePickerEx() {
39+
super();
40+
}
41+
42+
/**
43+
* Convenience constructor to create a date picker with a pre-selected date in current UI locale
44+
* format.
45+
*
46+
* @param initialDate the pre-selected date in the picker
47+
*/
48+
public DatePickerEx(LocalDate initialDate) {
49+
this();
50+
setValue(initialDate);
51+
}
52+
53+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import { css, html, LitElement } from 'lit';
2+
import { defineCustomElement } from '@vaadin/component-base/src/define.js';
3+
import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
4+
5+
export class FcDatePickerButtons extends ThemableMixin(LitElement) {
6+
static get is() {
7+
return 'fc-date-picker-buttons';
8+
}
9+
render() {
10+
return html`
11+
<button id="up"
12+
type="button"
13+
aria-label="Increment"
14+
@click=${this._clickUp}></button>
15+
<button id="down"
16+
type="button"
17+
aria-label="Decrement"
18+
@click=${this._clickDown}></button>
19+
`;
20+
}
21+
static get styles() {
22+
return css`
23+
:host {
24+
display: flex;
25+
flex-direction: column;
26+
align-self: stretch;
27+
justify-content: space-evenly;
28+
}
29+
button {
30+
padding: 0;
31+
margin: 0;
32+
max-height: 0.9rem;
33+
box-sizing: content-box;
34+
}
35+
button::before {
36+
display: block;
37+
}
38+
#up::before {
39+
content: "\\25B2";
40+
}
41+
#down::before {
42+
content: "\\25BC";
43+
}`;
44+
}
45+
connectedCallback() {
46+
super.connectedCallback();
47+
}
48+
_clickUp() {
49+
this.dispatchEvent(new CustomEvent("click-up"));
50+
}
51+
_clickDown() {
52+
this.dispatchEvent(new CustomEvent("click-down"));
53+
}
54+
}
55+
56+
defineCustomElement(FcDatePickerButtons);
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
import {DatePicker} from '@vaadin/date-picker/vaadin-date-picker.js';
2+
import { html } from '@polymer/polymer/polymer-element.js';
3+
import { defineCustomElement } from '@vaadin/component-base/src/define.js';
4+
import './fc-date-picker-buttons.js';
5+
6+
export class FcDatePicker extends DatePicker {
7+
static get is() {
8+
return 'fc-date-picker';
9+
}
10+
11+
static get template() {
12+
return html`
13+
${super.template}
14+
<style>
15+
vaadin-input-container {
16+
padding-left: 0;
17+
}
18+
::slotted(input) {
19+
padding-left: calc(0.575em + var(--lumo-border-radius-m) / 4 - 1px);
20+
}
21+
</style>
22+
`;
23+
}
24+
25+
ready() {
26+
const buttons=document.createElement('fc-date-picker-buttons');
27+
buttons.setAttribute('slot','prefix');
28+
this.appendChild(buttons);
29+
buttons.addEventListener("click-up", ()=>this._onButtonClick(+1));
30+
buttons.addEventListener("click-down", ()=>this._onButtonClick(-1));
31+
super.ready();
32+
}
33+
34+
_onButtonClick(delta) {
35+
// If input element contains a valid date
36+
const date = this.__parseDate(this.inputElement.value);
37+
if (date) {
38+
39+
// Count how many non-digit characters appear before the current cursor position
40+
let index=0;
41+
for (let i=0;i<this.inputElement.selectionStart;i++) {
42+
if (!this.inputElement.value[i].match(/[0-9]/)) index++;
43+
}
44+
45+
// Map the component index (e.g. 0 for first, 1 for second),
46+
// to its corresponding date part character ('M', 'D', or 'Y')
47+
// based on the formatted structure.
48+
const format = this.__formatDate(new Date(3333,10,22))
49+
.replaceAll('1','M')
50+
.replaceAll('2','D')
51+
.replaceAll('3','Y');
52+
const part = format.replace(new RegExp(`^([DMY]+[^DMY]){${index}}`),'')[0];
53+
54+
const incrementComponent = (date, part, delta) => {
55+
// Adjusts the specified component (year, month, or day) by the given delta,
56+
// and returns the clamped date in ISO format.
57+
const d = new Date(date);
58+
switch (part) {
59+
case 'Y': d.setFullYear(d.getFullYear() + delta); break;
60+
case 'M': d.setMonth (d.getMonth() + delta); break;
61+
case 'D': d.setDate (d.getDate() + delta); break;
62+
}
63+
return this._formatISO(d);
64+
};
65+
66+
this.value = incrementComponent(date, part, delta);
67+
68+
// Find the start of the index-th component
69+
let start=0;
70+
for (let i=0;i<index;start++) {
71+
if (!this.inputElement.value[start].match(/[0-9]/)) i++;
72+
}
73+
74+
// Find the end of the index-th component
75+
let end=start;
76+
while (end<this.inputElement.value.length && this.inputElement.value[end].match(/[0-9]/)) {
77+
end++;
78+
}
79+
80+
this.inputElement.selectionStart=start;
81+
this.inputElement.selectionEnd=end;
82+
83+
this.inputElement.focus();
84+
}
85+
}
86+
87+
}
88+
89+
defineCustomElement(FcDatePicker);
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*-
2+
* #%L
3+
* Year Month Calendar Add-on
4+
* %%
5+
* Copyright (C) 2021 - 2025 Flowing Code
6+
* %%
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
* #L%
19+
*/
20+
package com.flowingcode.addons.ycalendar;
21+
22+
import com.flowingcode.vaadin.addons.demo.DemoSource;
23+
import com.vaadin.flow.component.html.Div;
24+
import com.vaadin.flow.component.notification.Notification;
25+
import com.vaadin.flow.router.PageTitle;
26+
import com.vaadin.flow.router.Route;
27+
import java.time.LocalDate;
28+
import java.util.Objects;
29+
30+
@DemoSource
31+
@PageTitle("DatePicker")
32+
@Route(value = "year-month-calendar/date-picker", layout = YearMonthCalendarDemoView.class)
33+
public class DatePickerDemo extends Div {
34+
35+
public DatePickerDemo() {
36+
37+
DatePickerEx field = new DatePickerEx(LocalDate.now());
38+
// #if vaadin eq 0
39+
add(new LocaleSelector(field::setI18n));
40+
// #endif
41+
42+
field.addValueChangeListener(ev->{
43+
Notification.show(Objects.toString(ev.getValue()));
44+
});
45+
46+
add(field);
47+
}
48+
49+
}
50+

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ public YearMonthCalendarDemoView() {
3939
addDemo(YearI18NDemo.class);
4040
addDemo(YearReadonlyDemo.class);
4141
addDemo(MonthDemo.class);
42+
addDemo(DatePickerDemo.class);
4243
addDemo(InlineDatePickerDemo.class);
4344
addDemo(YearMonthFieldDemo.class);
4445
setSizeFull();

0 commit comments

Comments
 (0)