-
Notifications
You must be signed in to change notification settings - Fork 42
/
Copy pathdate-picker.js
228 lines (205 loc) · 8.66 KB
/
date-picker.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
/* pat-date-picker - Polyfill for input type=date */
import $ from "jquery";
import Base from "../../core/base";
import logging from "../../core/logging";
import Parser from "../../core/parser";
import dom from "../../core/dom";
import events from "../../core/events";
import utils from "../../core/utils";
const log = logging.getLogger("date-picker");
export const parser = new Parser("date-picker");
parser.addArgument("behavior", "styled", ["native", "styled", "keyboard"]);
parser.addArgument("week-numbers", [], ["show", "hide"]);
parser.addArgument("i18n"); // URL pointing to JSON resource with i18n values
parser.addArgument("first-day", 0);
parser.addArgument("after");
parser.addArgument("offset-days", 0);
parser.add_argument("output-format", null);
parser.add_argument("locale", null);
parser.addAlias("behaviour", "behavior");
/* JSON format for i18n
* { "previousMonth": "Previous Month",
* "nextMonth" : "Next Month",
* "months" : ["January","February","March","April","May","June","July","August","September","October","November","December"],
* "weekdays" : ["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],
* "weekdaysShort": ["Sun","Mon","Tue","Wed","Thu","Fri","Sat"]
* } */
export default Base.extend({
name: "date-picker",
trigger: ".pat-date-picker",
parser: parser,
format: "YYYY-MM-DD",
async init() {
const el = this.el;
const disabled = this.el.disabled;
//TODO: make parser with options extend missing options.
//this.options = parser.parse(el, opts);
this.options = $.extend(parser.parse(el), this.options);
if (!disabled && this.options.after) {
// Set the date depending on another date which must be ``offset-days``
// BEFORE this date. Only set it, if the other date is AFTER this
// date.
const befores = document.querySelectorAll(this.options.after);
for (const b_el of befores) {
b_el.addEventListener("change", (e) => {
let b_date = e.target.value; // the "before-date"
b_date = b_date ? new Date(b_date) : null;
if (!b_date) {
return;
}
let a_date = this.el.value; // the "after-date"
a_date = a_date ? new Date(a_date) : null;
if (!a_date || a_date < b_date) {
const offset = this.options.offsetDays || 0;
b_date.setDate(b_date.getDate() + offset);
this.el.value = b_date.toISOString().substring(0, 10);
this.dispatch_change_event();
}
});
}
}
let display_el;
if (this.options.behavior === "styled") {
dom.hide(el); // hide input, but keep active (e.g. for validation)
display_el = document.createElement("time");
display_el.setAttribute("class", "output-field");
display_el.setAttribute("datetime", el.value);
if (disabled) {
display_el.classList.add("disabled");
}
el.insertAdjacentElement("beforebegin", display_el);
// Disable click on label, as this invokes a click on the invisible
// input field which opens the calendar in Firefox and masks a
// click on display_el on Chrome.
const label = display_el.closest("label");
if (label) {
events.add_event_listener(
label,
"click",
"pat-date-picker--label",
(e) => {
e.preventDefault();
}
);
}
let pat_display_time;
if (this.options.outputFormat) {
const PatDisplayTime = (await import("../display-time/display-time")).default; // prettier-ignore
const display_time_config = { format: this.format };
if (this.options.outputFormat) {
display_time_config["output-format"] = this.options.outputFormat;
}
if (this.options.locale) {
display_time_config.locale = this.options.locale;
}
pat_display_time = new PatDisplayTime(display_el, display_time_config);
} else if (this.el.value) {
display_el.textContent = el.value;
}
// Add the additional elements "clear button" and placeholder to
// the `<time>` element.
const _add_additional = () => {
this.add_clear_button(display_el);
if (!this.el.value && this.el.placeholder) {
display_el.innerHTML = `<span class="placeholder">${this.el.placeholder}</span>`;
}
};
if (pat_display_time) {
// Add the additional elements after display time has
// eventually cleared the contents or done any other changes.
$(display_el).on("init.display-time.patterns", () => {
_add_additional();
});
} else {
// If no `pat-display-time` was used, add immediately.
_add_additional();
}
this.el.addEventListener("change", () => {
display_el.setAttribute("datetime", this.el.value);
if (pat_display_time && this.el.value) {
pat_display_time.format();
} else {
//} else if (this.el.value) {
display_el.textContent = this.el.value;
}
_add_additional();
});
if (disabled) {
// nothing else to do here...
return;
}
} else if (disabled) {
return;
} else if (
this.options.behavior !== "keyboard" &&
utils.checkInputSupport("date", "invalid date")
) {
// behavior native with native support.
return;
} else if (el.getAttribute("type") === "date") {
// behavior native but no native support.
// Fallback JS date picker with a text input field.
el.setAttribute("type", "text");
}
if (window.__patternslib_import_styles) {
import("./_date-picker.scss");
}
const Pikaday = (await import("pikaday")).default;
const config = {
field: el,
trigger: display_el || el,
format: this.format,
firstDay: this.options.firstDay,
showWeekNumber: this.options.weekNumbers === "show",
onSelect: () => this.dispatch_change_event(),
onClose: () => {
if (this.options.behavior === "styled" && !this.el.value) {
// blur the input field so that pat-validate can kick in when
// nothing was selected.
el.dispatchEvent(events.blur_event());
}
},
};
if (el.getAttribute("min")) {
config.minDate = new Date(el.getAttribute("min"));
}
if (el.getAttribute("max")) {
config.maxDate = new Date(el.getAttribute("max"));
}
if (this.options.i18n) {
try {
const response = await fetch(this.options.i18n);
config.i18n = await response.json();
} catch {
log.error(`date-picker could not load i18n for ${this.options.i18n}`);
}
}
this.pikaday = new Pikaday(config);
},
add_clear_button(el_append_to) {
if (!this.el.disabled && !this.el.required && this.el.value) {
// Add clear button
const clear_button = document.createElement("span");
clear_button.setAttribute("class", "cancel-button");
clear_button.addEventListener("click", (e) => {
e.stopPropagation();
this.el.value = null;
this.dispatch_change_event();
});
el_append_to.appendChild(clear_button);
}
},
dispatch_change_event() {
const event = new Event("change", {
bubbles: true,
cancelable: true,
});
// Set ``firedBy` to prevent pikaday to call it's own handler and land
// in an infinite loop.
event.firedBy = this.pikaday;
this.el.dispatchEvent(event);
// Also trigger input-change
$(this.el).trigger("input-change");
$(this.el.form).trigger("input-change");
},
});