imx-select-datepicker 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +64 -0
- package/index.js +1 -0
- package/package.json +23 -0
- package/src/InputGroup/InputGroup.js +75 -0
- package/src/InputGroup/utils/DateHelper.js +13 -0
- package/src/InputGroup/utils/DaySelect.js +64 -0
- package/src/InputGroup/utils/DomHelper.js +11 -0
- package/src/InputGroup/utils/MonthSelect.js +42 -0
- package/src/InputGroup/utils/Option.js +18 -0
- package/src/InputGroup/utils/OptionGroup.js +27 -0
- package/src/InputGroup/utils/Select.js +11 -0
package/README.md
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# imx-select-datepicker - InputGroup
|
|
2
|
+
|
|
3
|
+
A component for managing date inputs with separate controls for day and month.
|
|
4
|
+
|
|
5
|
+
## Constructor
|
|
6
|
+
|
|
7
|
+
```javascript
|
|
8
|
+
const inputGroup = new InputGroup(startDate, endDate);
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
**Parameters:**
|
|
12
|
+
- `startDate` (Date, optional) - The start date for the possible select options. Defaults to the current date minus 1 year.
|
|
13
|
+
- `endDate` (Date, optional) - The end date for the possible select options. Defaults to the current date plus 1 year.
|
|
14
|
+
|
|
15
|
+
## Usage as single date picker
|
|
16
|
+
|
|
17
|
+
```javascript
|
|
18
|
+
const inputGroup = new InputGroup();
|
|
19
|
+
inputDate.appendChild(inputGroup.markup);
|
|
20
|
+
|
|
21
|
+
inputGroup.update(new Date('2026-01-01'));
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Usage as range date picker
|
|
25
|
+
|
|
26
|
+
```javascript
|
|
27
|
+
const inputGroupFrom = new InputGroup();
|
|
28
|
+
inputDate.appendChild(inputGroupFrom.markup);
|
|
29
|
+
|
|
30
|
+
const inputGroupTo = new InputGroup();
|
|
31
|
+
inputDate.appendChild(inputGroupTo.markup);
|
|
32
|
+
|
|
33
|
+
inputGroupFrom.update(new Date('2026-01-01'));
|
|
34
|
+
inputGroupTo.update(new Date('2026-12-31'));
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Properties
|
|
38
|
+
|
|
39
|
+
- **`markup`** - The DOM element of the component that can be inserted into the document
|
|
40
|
+
- **`daySelect.select`** - Select element for day selection, to register a change listener.
|
|
41
|
+
- **`monthSelect.select`** - Select element for month selection, to register a change listener.
|
|
42
|
+
|
|
43
|
+
## Methods
|
|
44
|
+
|
|
45
|
+
### `update(date)`
|
|
46
|
+
Updates the input fields with a new date.
|
|
47
|
+
|
|
48
|
+
**Parameters:**
|
|
49
|
+
- `date` (Date) - The new date
|
|
50
|
+
|
|
51
|
+
### `setMinDate(date)`
|
|
52
|
+
Sets the minimum selectable date for the input fields.
|
|
53
|
+
|
|
54
|
+
**Parameters:**
|
|
55
|
+
- `date` (Date) - The minimum date
|
|
56
|
+
|
|
57
|
+
## Events
|
|
58
|
+
|
|
59
|
+
```javascript
|
|
60
|
+
inputGroupFrom.daySelect.select.addEventListener('change', (event) => {
|
|
61
|
+
const newDate = new Date(event.target.value);
|
|
62
|
+
console.log('New date:', newDate);
|
|
63
|
+
});
|
|
64
|
+
```
|
package/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { InputGroup } from "./src/InputGroup/InputGroup.js";
|
package/package.json
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "imx-select-datepicker",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "A simple keyboard accessible select based datepicker",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"accessibility",
|
|
7
|
+
"datepicker"
|
|
8
|
+
],
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git@gitlab.infomax.dev:haunerdinger/imx-select-datepicker.git"
|
|
12
|
+
},
|
|
13
|
+
"license": "ISC",
|
|
14
|
+
"author": "Markus Haunerdinger",
|
|
15
|
+
"type": "module",
|
|
16
|
+
"main": "index.js",
|
|
17
|
+
"scripts": {
|
|
18
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
19
|
+
},
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"dayjs": "^1.11.19"
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import dayjs from 'dayjs';
|
|
2
|
+
import { OptionGroup } from './utils/OptionGroup.js';
|
|
3
|
+
import { DaySelect } from './utils/DaySelect.js';
|
|
4
|
+
import { DomHelper } from './utils/DomHelper.js';
|
|
5
|
+
import { DateHelper } from './utils/DateHelper.js';
|
|
6
|
+
import { MonthSelect } from './utils/MonthSelect.js';
|
|
7
|
+
|
|
8
|
+
export class InputGroup {
|
|
9
|
+
|
|
10
|
+
constructor(startDate = dayjs().subtract(1, 'y').toDate(), endDate = dayjs().add(1, 'y').toDate()) {
|
|
11
|
+
const startOfMonth = dayjs(startDate).startOf('month');
|
|
12
|
+
const endOfMonth = dayjs(endDate).endOf('month');
|
|
13
|
+
|
|
14
|
+
this.optionGroups = OptionGroup.createOptions(
|
|
15
|
+
DateHelper.range(startOfMonth, endOfMonth, 'month')
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
this.daySelect = new DaySelect();
|
|
19
|
+
this.monthSelect = new MonthSelect(this.optionGroups);
|
|
20
|
+
|
|
21
|
+
this.registerEventListeners(this.monthSelect);
|
|
22
|
+
|
|
23
|
+
this.markup = DomHelper.createMarkup(this.monthSelect.select, this.daySelect.select);
|
|
24
|
+
this.updateDayOptions(startDate);
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
setMinDate(jsDateObject) {
|
|
28
|
+
this.monthSelect.setMinDate(jsDateObject);
|
|
29
|
+
this.daySelect.setMinDate(jsDateObject);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
getValue() {
|
|
33
|
+
return this.daySelect.select.value;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
update(jsDateObject) {
|
|
37
|
+
if(dayjs(jsDateObject).isValid()) {
|
|
38
|
+
this.updateMonth(jsDateObject);
|
|
39
|
+
this.updateDayOptions(jsDateObject);
|
|
40
|
+
this.updateDay(jsDateObject);
|
|
41
|
+
} else {
|
|
42
|
+
console.warn('Update with invalid date:', jsDateObject);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
updateDay(jsDateObject) {
|
|
47
|
+
const dayJsDate = dayjs(jsDateObject);
|
|
48
|
+
this.daySelect.setValue(dayJsDate);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
updateMonth(jsDateObject) {
|
|
52
|
+
const dayJsDate = dayjs(jsDateObject);
|
|
53
|
+
|
|
54
|
+
const optionGroup = this.optionGroups.find((optionGroup) => {
|
|
55
|
+
return dayjs(optionGroup.index).isSame(dayJsDate, 'month');
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
if(optionGroup) {
|
|
59
|
+
this.monthSelect.setValue(optionGroup.index);
|
|
60
|
+
} else {
|
|
61
|
+
console.warn('No matching month option found for date:', jsDateObject);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
updateDayOptions(jsDateObject) {
|
|
66
|
+
const dayJsDate = dayjs(jsDateObject)
|
|
67
|
+
this.daySelect.updateOptions(dayJsDate);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
registerEventListeners(monthSelect) {
|
|
71
|
+
monthSelect.select.addEventListener('change', (event) => {
|
|
72
|
+
this.daySelect.updateOptions(dayjs(event.target.value));
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export class DateHelper {
|
|
2
|
+
static range(start, end, interval){
|
|
3
|
+
let date = start;
|
|
4
|
+
const dateArray = [];
|
|
5
|
+
|
|
6
|
+
while(date.isBefore(end, interval) || date.isSame(end, interval)) {
|
|
7
|
+
dateArray.push(date)
|
|
8
|
+
date = date.add(1, interval);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
return dateArray;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import dayjs from 'dayjs';
|
|
2
|
+
import { Select } from './Select.js';
|
|
3
|
+
import { OptionGroup } from './OptionGroup.js';
|
|
4
|
+
|
|
5
|
+
export class DaySelect {
|
|
6
|
+
constructor() {
|
|
7
|
+
this._select = Select.createDaySelect();
|
|
8
|
+
this._minDate = null;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
get select() {
|
|
12
|
+
return this._select;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
set select(node) {
|
|
16
|
+
this._select = node;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
getOptions() {
|
|
20
|
+
return Array.from(this._select.options);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
setValue(dayJsDate) {
|
|
24
|
+
this._select.value = dayJsDate.format('YYYY-MM-DD');
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
enableOptions() {
|
|
28
|
+
this.getOptions().forEach((option) => {
|
|
29
|
+
option.disabled = false;
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
setMinDate(jsDateObject) {
|
|
34
|
+
if(!jsDateObject) {
|
|
35
|
+
this._minDate = null;
|
|
36
|
+
} else {
|
|
37
|
+
this._minDate = dayjs(jsDateObject).format('YYYY-MM-DD');
|
|
38
|
+
}
|
|
39
|
+
this._evalMinDate();
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
_evalMinDate() {
|
|
43
|
+
if(!this._minDate) {
|
|
44
|
+
this.enableOptions();
|
|
45
|
+
} else {
|
|
46
|
+
const minDate = dayjs(this._minDate);
|
|
47
|
+
|
|
48
|
+
this.getOptions().forEach((option) => {
|
|
49
|
+
option.disabled = dayjs(option.value).isBefore(minDate, 'day');
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
updateOptions(dayJsDate) {
|
|
55
|
+
const dayOptions = OptionGroup.createDayOptions(dayJsDate);
|
|
56
|
+
|
|
57
|
+
if(dayOptions.length > 0) {
|
|
58
|
+
this._select.innerHTML = dayOptions.map((day) => day.outerHTML).join('');
|
|
59
|
+
this._evalMinDate();
|
|
60
|
+
} else {
|
|
61
|
+
console.warn('No options found for:', dayJsDate);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export class DomHelper {
|
|
2
|
+
static createMarkup(daySelect, monthSelect) {
|
|
3
|
+
const wrapper = document.createElement("div");
|
|
4
|
+
wrapper.classList.add("inputGroup");
|
|
5
|
+
|
|
6
|
+
wrapper.appendChild(daySelect);
|
|
7
|
+
wrapper.appendChild(monthSelect);
|
|
8
|
+
|
|
9
|
+
return wrapper;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import dayjs from 'dayjs';
|
|
2
|
+
import { Select } from './Select.js';
|
|
3
|
+
import { OptionGroup } from './OptionGroup.js';
|
|
4
|
+
|
|
5
|
+
export class MonthSelect {
|
|
6
|
+
constructor(optionGroups) {
|
|
7
|
+
this._select = Select.createMonthSelect(OptionGroup.getMonthOptions(optionGroups))
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
get select() {
|
|
11
|
+
return this._select;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
set select(node) {
|
|
15
|
+
this._select = node;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
getOptions() {
|
|
19
|
+
return Array.from(this._select.options);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
setValue(dateString) {
|
|
23
|
+
this._select.value = dateString;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
enableOptions() {
|
|
27
|
+
this.getOptions().forEach((option) => {
|
|
28
|
+
option.disabled = false;
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
setMinDate(jsDateObject) {
|
|
33
|
+
if(!jsDateObject) {
|
|
34
|
+
this.enableOptions();
|
|
35
|
+
} else {
|
|
36
|
+
const dayJsDate = dayjs(jsDateObject);
|
|
37
|
+
this.getOptions().forEach((option) => {
|
|
38
|
+
option.disabled = dayJsDate.isAfter(dayjs(option.value), 'month');
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export class Option {
|
|
2
|
+
static createMonthOption(date) {
|
|
3
|
+
const formatted = date.format('MMMM YYYY');
|
|
4
|
+
return this.createOption(date.format('YYYY-MM-DD'), formatted);
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
static createDayOption(date) {
|
|
8
|
+
const formatted = date.format('ddd D');
|
|
9
|
+
return this.createOption(date.format('YYYY-MM-DD'), formatted);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
static createOption(value, text) {
|
|
13
|
+
const option = document.createElement("option");
|
|
14
|
+
option.value = value;
|
|
15
|
+
option.textContent = text;
|
|
16
|
+
return option;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Option } from './Option.js';
|
|
2
|
+
import { DateHelper } from './DateHelper.js';
|
|
3
|
+
|
|
4
|
+
export class OptionGroup {
|
|
5
|
+
static createOptions(months) {
|
|
6
|
+
const options = [];
|
|
7
|
+
|
|
8
|
+
months.forEach((month) => {
|
|
9
|
+
options.push({
|
|
10
|
+
index: month.format('YYYY-MM-DD'),
|
|
11
|
+
month: Option.createMonthOption(month)
|
|
12
|
+
});
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
return options;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
static createDayOptions(dayJsDate) {
|
|
19
|
+
const days = DateHelper.range(dayJsDate.subtract(1, 'month'), dayJsDate.add(1, 'month'), 'day');
|
|
20
|
+
const daysOfMonth = days.filter((day) => dayJsDate.isSame(day, 'month'));
|
|
21
|
+
return daysOfMonth.map((day) => Option.createDayOption(day));
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
static getMonthOptions(optionGroups) {
|
|
25
|
+
return optionGroups.map((group) => group.month.outerHTML).join('');
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export class Select {
|
|
2
|
+
static createDaySelect() {
|
|
3
|
+
return document.createElement("select");
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
static createMonthSelect(monthOptions) {
|
|
7
|
+
const monthSelect = document.createElement("select");
|
|
8
|
+
monthSelect.innerHTML = monthOptions;
|
|
9
|
+
return monthSelect;
|
|
10
|
+
}
|
|
11
|
+
}
|