@wernfried/daterangepicker 5.2.0 → 5.2.2
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/API_Doc.md +2 -2
- package/README.md +8 -8
- package/dist/cjs/daterangepicker.js +2367 -0
- package/dist/cjs/daterangepicker.js.map +1 -0
- package/dist/cjs/daterangepicker.min.js +2 -0
- package/dist/cjs/daterangepicker.min.js.map +1 -0
- package/dist/esm/daterangepicker.js +4 -4
- package/dist/esm/daterangepicker.js.map +1 -1
- package/dist/esm/daterangepicker.min.js +1 -1
- package/dist/esm/daterangepicker.min.js.map +1 -1
- package/dist/global/daterangepicker.js +4 -4
- package/dist/global/daterangepicker.js.map +1 -1
- package/dist/global/daterangepicker.min.js +1 -1
- package/dist/global/daterangepicker.min.js.map +1 -1
- package/package.json +7 -2
|
@@ -0,0 +1,2367 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var luxon = require("luxon");
|
|
3
|
+
class DateRangePicker {
|
|
4
|
+
#startDate = null;
|
|
5
|
+
#endDate = null;
|
|
6
|
+
constructor(element, options, cb) {
|
|
7
|
+
if (typeof element === "string" && document.querySelectorAll(element).length > 1)
|
|
8
|
+
throw new RangeError(`Option 'element' must match to one element only`);
|
|
9
|
+
this.parentEl = "body";
|
|
10
|
+
this.element = element instanceof HTMLElement ? element : document.querySelector(element);
|
|
11
|
+
this.isInputText = this.element instanceof HTMLInputElement && this.element.type === "text";
|
|
12
|
+
this.#startDate = luxon.DateTime.now().startOf("day");
|
|
13
|
+
this.#endDate = luxon.DateTime.now().plus({ day: 1 }).startOf("day");
|
|
14
|
+
this.minDate = null;
|
|
15
|
+
this.maxDate = null;
|
|
16
|
+
this.maxSpan = null;
|
|
17
|
+
this.minSpan = null;
|
|
18
|
+
this.defaultSpan = null;
|
|
19
|
+
this.initalMonth = luxon.DateTime.now().startOf("month");
|
|
20
|
+
this.autoApply = false;
|
|
21
|
+
this.singleDatePicker = false;
|
|
22
|
+
this.singleMonthView = false;
|
|
23
|
+
this.showDropdowns = false;
|
|
24
|
+
this.minYear = luxon.DateTime.now().minus({ year: 100 }).year;
|
|
25
|
+
this.maxYear = luxon.DateTime.now().plus({ year: 100 }).year;
|
|
26
|
+
this.showWeekNumbers = false;
|
|
27
|
+
this.showISOWeekNumbers = false;
|
|
28
|
+
this.showCustomRangeLabel = true;
|
|
29
|
+
this.showLabel = !this.isInputText;
|
|
30
|
+
this.timePicker = false;
|
|
31
|
+
const usesMeridiems = new Intl.DateTimeFormat(luxon.DateTime.now().locale, { hour: "numeric" }).resolvedOptions();
|
|
32
|
+
this.timePicker24Hour = !usesMeridiems.hour12;
|
|
33
|
+
this.timePickerStepSize = luxon.Duration.fromObject({ minutes: 1 });
|
|
34
|
+
this.linkedCalendars = true;
|
|
35
|
+
this.autoUpdateInput = true;
|
|
36
|
+
this.alwaysShowCalendars = false;
|
|
37
|
+
this.isInvalidDate = null;
|
|
38
|
+
this.isInvalidTime = null;
|
|
39
|
+
this.isCustomDate = null;
|
|
40
|
+
this.onOutsideClick = "apply";
|
|
41
|
+
this.opens = this.element?.classList.contains("pull-right") ? "left" : "right";
|
|
42
|
+
this.drops = this.element?.classList.contains("dropup") ? "up" : "down";
|
|
43
|
+
this.buttonClasses = "btn btn-sm";
|
|
44
|
+
this.applyButtonClasses = "btn-primary";
|
|
45
|
+
this.cancelButtonClasses = "btn-default";
|
|
46
|
+
this.weekendClasses = "weekend";
|
|
47
|
+
this.weekendDayClasses = "weekend-day";
|
|
48
|
+
this.todayClasses = "today";
|
|
49
|
+
this.altInput = null;
|
|
50
|
+
this.altFormat = null;
|
|
51
|
+
this.externalStyle = null;
|
|
52
|
+
this.ranges = {};
|
|
53
|
+
this.locale = {
|
|
54
|
+
direction: "ltr",
|
|
55
|
+
format: luxon.DateTime.DATE_SHORT,
|
|
56
|
+
// or DateTime.DATETIME_SHORT when timePicker: true
|
|
57
|
+
separator: " - ",
|
|
58
|
+
applyLabel: "Apply",
|
|
59
|
+
cancelLabel: "Cancel",
|
|
60
|
+
weekLabel: "W",
|
|
61
|
+
customRangeLabel: "Custom Range",
|
|
62
|
+
daysOfWeek: luxon.Info.weekdays("short"),
|
|
63
|
+
monthNames: luxon.Info.months("long"),
|
|
64
|
+
firstDay: luxon.Info.getStartOfWeek(),
|
|
65
|
+
durationFormat: null
|
|
66
|
+
};
|
|
67
|
+
if (this.element == null)
|
|
68
|
+
return;
|
|
69
|
+
this.callback = function() {
|
|
70
|
+
};
|
|
71
|
+
this.isShowing = false;
|
|
72
|
+
this.leftCalendar = {};
|
|
73
|
+
this.rightCalendar = {};
|
|
74
|
+
if (typeof options !== "object" || options === null)
|
|
75
|
+
options = {};
|
|
76
|
+
let dataOptions = {};
|
|
77
|
+
const data = Array.from(this.element.attributes).filter((x) => x.name.startsWith("data-"));
|
|
78
|
+
for (let item of data) {
|
|
79
|
+
const name = item.name.replace(/^data-/g, "").replace(/-([a-z])/g, function(str) {
|
|
80
|
+
return str[1].toUpperCase();
|
|
81
|
+
});
|
|
82
|
+
if (!Object.keys(this).concat(["startDate", "endDate"]).includes(name) || Object.keys(options).includes(name))
|
|
83
|
+
continue;
|
|
84
|
+
let ts = luxon.DateTime.fromISO(item.value);
|
|
85
|
+
const isDate = ["startDate", "endDate", "minDate", "maxDate", "initalMonth"].includes(name);
|
|
86
|
+
dataOptions[name] = ts.isValid && isDate ? ts : JSON.parse(item.value);
|
|
87
|
+
}
|
|
88
|
+
options = { ...dataOptions, ...options };
|
|
89
|
+
if (typeof options.singleDatePicker === "boolean")
|
|
90
|
+
this.singleDatePicker = options.singleDatePicker;
|
|
91
|
+
if (!this.singleDatePicker && typeof options.singleMonthView === "boolean") {
|
|
92
|
+
this.singleMonthView = options.singleMonthView;
|
|
93
|
+
} else {
|
|
94
|
+
this.singleMonthView = false;
|
|
95
|
+
}
|
|
96
|
+
if (!(options.externalStyle === null)) {
|
|
97
|
+
const bodyStyle = window.getComputedStyle(document.body);
|
|
98
|
+
if (bodyStyle && typeof bodyStyle[Symbol.iterator] === "function" && [...bodyStyle].some((x) => x.startsWith("--bulma-")))
|
|
99
|
+
this.externalStyle = "bulma";
|
|
100
|
+
}
|
|
101
|
+
if (typeof options.template === "string" || options.template instanceof HTMLElement) {
|
|
102
|
+
this.container = typeof options.template === "string" ? createElementFromHTML(options.template) : options.template;
|
|
103
|
+
} else {
|
|
104
|
+
let template = [
|
|
105
|
+
'<div class="daterangepicker">',
|
|
106
|
+
'<div class="ranges"></div>',
|
|
107
|
+
'<div class="drp-calendar left">',
|
|
108
|
+
'<table class="calendar-table">',
|
|
109
|
+
"<thead></thead>",
|
|
110
|
+
"<tbody></tbody>",
|
|
111
|
+
"<tfoot>",
|
|
112
|
+
'<tr class="calendar-time start-time"></tr>',
|
|
113
|
+
this.singleMonthView ? '<tr class="calendar-time end-time"></tr>' : "",
|
|
114
|
+
"</tfoot>",
|
|
115
|
+
"</table>",
|
|
116
|
+
"</div>"
|
|
117
|
+
];
|
|
118
|
+
template.push(...[
|
|
119
|
+
'<div class="drp-calendar right">',
|
|
120
|
+
'<table class="calendar-table">',
|
|
121
|
+
"<thead></thead>",
|
|
122
|
+
"<tbody></tbody>",
|
|
123
|
+
"<tfoot>",
|
|
124
|
+
this.singleMonthView ? "" : '<tr class="calendar-time end-time"></tr>',
|
|
125
|
+
"</tfoot>",
|
|
126
|
+
"</table>",
|
|
127
|
+
"</div>"
|
|
128
|
+
]);
|
|
129
|
+
template.push(...[
|
|
130
|
+
'<div class="drp-buttons">',
|
|
131
|
+
'<div class="drp-duration-label"></div>',
|
|
132
|
+
'<div class="drp-selected"></div>'
|
|
133
|
+
]);
|
|
134
|
+
if (this.externalStyle === "bulma") {
|
|
135
|
+
template.push(...[
|
|
136
|
+
'<div class="buttons">',
|
|
137
|
+
'<button class="cancelBtn button is-small" type="button"></button>',
|
|
138
|
+
'<button class="applyBtn button is-small" disabled type="button"></button>',
|
|
139
|
+
"</div>"
|
|
140
|
+
]);
|
|
141
|
+
} else {
|
|
142
|
+
template.push(...[
|
|
143
|
+
"<div>",
|
|
144
|
+
'<button class="cancelBtn" type="button"></button>',
|
|
145
|
+
'<button class="applyBtn" disabled type="button"></button>',
|
|
146
|
+
"</div>"
|
|
147
|
+
]);
|
|
148
|
+
}
|
|
149
|
+
template.push("</div></div>");
|
|
150
|
+
options.template = template.join("");
|
|
151
|
+
this.container = createElementFromHTML(options.template);
|
|
152
|
+
}
|
|
153
|
+
this.parentEl = document.querySelector(typeof options.parentEl === "string" ? options.parentEl : this.parentEl);
|
|
154
|
+
this.parentEl.appendChild(this.container);
|
|
155
|
+
if (typeof options.timePicker === "boolean")
|
|
156
|
+
this.timePicker = options.timePicker;
|
|
157
|
+
if (this.timePicker)
|
|
158
|
+
this.locale.format = luxon.DateTime.DATETIME_SHORT;
|
|
159
|
+
if (typeof options.locale === "object") {
|
|
160
|
+
for (let key2 of ["separator", "applyLabel", "cancelLabel", "weekLabel"]) {
|
|
161
|
+
if (typeof options.locale[key2] === "string")
|
|
162
|
+
this.locale[key2] = options.locale[key2];
|
|
163
|
+
}
|
|
164
|
+
if (typeof options.locale.direction === "string") {
|
|
165
|
+
if (["rtl", "ltr"].includes(options.locale.direction))
|
|
166
|
+
this.locale.direction = options.locale.direction;
|
|
167
|
+
else
|
|
168
|
+
console.error(`Option 'options.locale.direction' must be 'rtl' or 'ltr'`);
|
|
169
|
+
}
|
|
170
|
+
if (["string", "object"].includes(typeof options.locale.format))
|
|
171
|
+
this.locale.format = options.locale.format;
|
|
172
|
+
if (Array.isArray(options.locale.daysOfWeek)) {
|
|
173
|
+
if (options.locale.daysOfWeek.some((x) => typeof x !== "string"))
|
|
174
|
+
console.error(`Option 'options.locale.daysOfWeek' must be an array of strings`);
|
|
175
|
+
else
|
|
176
|
+
this.locale.daysOfWeek = options.locale.daysOfWeek.slice();
|
|
177
|
+
}
|
|
178
|
+
if (Array.isArray(options.locale.monthNames)) {
|
|
179
|
+
if (options.locale.monthNames.some((x) => typeof x !== "string"))
|
|
180
|
+
console.error(`Option 'locale.monthNames' must be an array of strings`);
|
|
181
|
+
else
|
|
182
|
+
this.locale.monthNames = options.locale.monthNames.slice();
|
|
183
|
+
}
|
|
184
|
+
if (typeof options.locale.firstDay === "number")
|
|
185
|
+
this.locale.firstDay = options.locale.firstDay;
|
|
186
|
+
if (typeof options.locale.customRangeLabel === "string") {
|
|
187
|
+
var elem = document.createElement("textarea");
|
|
188
|
+
elem.innerHTML = options.locale.customRangeLabel;
|
|
189
|
+
var rangeHtml = elem.value;
|
|
190
|
+
this.locale.customRangeLabel = rangeHtml;
|
|
191
|
+
}
|
|
192
|
+
if (["string", "object", "function"].includes(typeof options.locale.durationFormat) && options.locale.durationFormat != null)
|
|
193
|
+
this.locale.durationFormat = options.locale.durationFormat;
|
|
194
|
+
}
|
|
195
|
+
this.container.classList.add(this.locale.direction);
|
|
196
|
+
for (let key2 of [
|
|
197
|
+
"timePicker24Hour",
|
|
198
|
+
"showWeekNumbers",
|
|
199
|
+
"showISOWeekNumbers",
|
|
200
|
+
"showDropdowns",
|
|
201
|
+
"linkedCalendars",
|
|
202
|
+
"showCustomRangeLabel",
|
|
203
|
+
"alwaysShowCalendars",
|
|
204
|
+
"autoApply",
|
|
205
|
+
"autoUpdateInput",
|
|
206
|
+
"showLabel"
|
|
207
|
+
]) {
|
|
208
|
+
if (typeof options[key2] === "boolean")
|
|
209
|
+
this[key2] = options[key2];
|
|
210
|
+
}
|
|
211
|
+
for (let key2 of ["applyButtonClasses", "cancelButtonClasses", "weekendClasses", "weekendDayClasses", "todayClasses"]) {
|
|
212
|
+
if (typeof options[key2] === "string") {
|
|
213
|
+
this[key2] = options[key2];
|
|
214
|
+
} else if (["weekendClasses", "weekendDayClasses", "todayClasses"].includes(key2) && options[key2] === null) {
|
|
215
|
+
this[key2] = options[key2];
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
for (let key2 of ["minYear", "maxYear"]) {
|
|
219
|
+
if (typeof options[key2] === "number")
|
|
220
|
+
this[key2] = options[key2];
|
|
221
|
+
}
|
|
222
|
+
for (let key2 of ["isInvalidDate", "isInvalidTime", "isCustomDate"]) {
|
|
223
|
+
if (typeof options[key2] === "function")
|
|
224
|
+
this[key2] = options[key2];
|
|
225
|
+
else
|
|
226
|
+
this[key2] = function() {
|
|
227
|
+
return false;
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
if (!this.singleDatePicker) {
|
|
231
|
+
for (let opt of ["minSpan", "maxSpan", "defaultSpan"]) {
|
|
232
|
+
if (["string", "number", "object"].includes(typeof options[opt])) {
|
|
233
|
+
if (luxon.Duration.isDuration(options[opt]) && options[opt].isValid) {
|
|
234
|
+
this[opt] = options[opt];
|
|
235
|
+
} else if (luxon.Duration.fromISO(options[opt]).isValid) {
|
|
236
|
+
this[opt] = luxon.Duration.fromISO(options[opt]);
|
|
237
|
+
} else if (typeof options[opt] === "number" && luxon.Duration.fromObject({ seconds: options[opt] }).isValid) {
|
|
238
|
+
this[opt] = luxon.Duration.fromObject({ seconds: options[opt] });
|
|
239
|
+
} else if (options[opt] === null) {
|
|
240
|
+
this[opt] = null;
|
|
241
|
+
} else {
|
|
242
|
+
console.error(`Option '${key}' is not valid`);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
if (this.minSpan && this.maxSpan && this.minSpan > this.maxSpan) {
|
|
247
|
+
this.minSpan = null;
|
|
248
|
+
this.maxSpan = null;
|
|
249
|
+
console.warn(`Ignore option 'minSpan' and 'maxSpan', because 'minSpan' must be smaller than 'maxSpan'`);
|
|
250
|
+
}
|
|
251
|
+
if (this.defaultSpan && this.minSpan && this.minSpan > this.defaultSpan) {
|
|
252
|
+
this.defaultSpan = null;
|
|
253
|
+
console.warn(`Ignore option 'defaultSpan', because 'defaultSpan' must be greater than 'minSpan'`);
|
|
254
|
+
} else if (this.defaultSpan && this.maxSpan && this.maxSpan < this.defaultSpan) {
|
|
255
|
+
this.defaultSpan = null;
|
|
256
|
+
console.warn(`Ignore option 'defaultSpan', because 'defaultSpan' must be smaller than 'maxSpan'`);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
if (this.timePicker) {
|
|
260
|
+
if (["string", "object", "number"].includes(typeof options.timePickerStepSize)) {
|
|
261
|
+
let duration;
|
|
262
|
+
if (luxon.Duration.isDuration(options.timePickerStepSize) && options.timePickerStepSize.isValid) {
|
|
263
|
+
duration = options.timePickerStepSize;
|
|
264
|
+
} else if (luxon.Duration.fromISO(options.timePickerStepSize).isValid) {
|
|
265
|
+
duration = luxon.Duration.fromISO(options.timePickerStepSize);
|
|
266
|
+
} else if (typeof options.timePickerStepSize === "number" && luxon.Duration.fromObject({ seconds: options.timePickerStepSize }).isValid) {
|
|
267
|
+
duration = luxon.Duration.fromObject({ seconds: options.timePickerStepSize });
|
|
268
|
+
} else {
|
|
269
|
+
console.error(`Option 'timePickerStepSize' is not valid`);
|
|
270
|
+
duration = this.timePickerStepSize;
|
|
271
|
+
}
|
|
272
|
+
var valid = [];
|
|
273
|
+
for (let unit of ["minutes", "seconds"])
|
|
274
|
+
valid.push(...[1, 2, 3, 4, 5, 6, 10, 12, 15, 20, 30].map((x) => {
|
|
275
|
+
return luxon.Duration.fromObject({ [unit]: x });
|
|
276
|
+
}));
|
|
277
|
+
valid.push(...[1, 2, 3, 4, 6].map((x) => {
|
|
278
|
+
return luxon.Duration.fromObject({ hours: x });
|
|
279
|
+
}));
|
|
280
|
+
if (this.timePicker24Hour)
|
|
281
|
+
valid.push(...[8, 12].map((x) => {
|
|
282
|
+
return luxon.Duration.fromObject({ hours: x });
|
|
283
|
+
}));
|
|
284
|
+
if (valid.some((x) => duration.rescale().equals(x))) {
|
|
285
|
+
this.timePickerStepSize = duration.rescale();
|
|
286
|
+
} else {
|
|
287
|
+
console.error(`Option 'timePickerStepSize' ${JSON.stringify(duration.toObject())} is not valid`);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
if (this.maxSpan && this.timePickerStepSize > this.maxSpan)
|
|
291
|
+
console.error(`Option 'timePickerStepSize' ${JSON.stringify(this.timePickerStepSize.toObject())} must be smaller than 'maxSpan'`);
|
|
292
|
+
this.timePickerOpts = {
|
|
293
|
+
showMinutes: this.timePickerStepSize < luxon.Duration.fromObject({ hours: 1 }),
|
|
294
|
+
showSeconds: this.timePickerStepSize < luxon.Duration.fromObject({ minutes: 1 }),
|
|
295
|
+
hourStep: this.timePickerStepSize >= luxon.Duration.fromObject({ hours: 1 }) ? this.timePickerStepSize.hours : 1,
|
|
296
|
+
minuteStep: this.timePickerStepSize >= luxon.Duration.fromObject({ minutes: 1 }) ? this.timePickerStepSize.minutes : 1,
|
|
297
|
+
secondStep: this.timePickerStepSize.seconds
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
for (let opt of ["startDate", "endDate", "minDate", "maxDate", "initalMonth"]) {
|
|
301
|
+
if (opt === "endDate" && this.singleDatePicker)
|
|
302
|
+
continue;
|
|
303
|
+
if (typeof options[opt] === "object") {
|
|
304
|
+
if (luxon.DateTime.isDateTime(options[opt]) && options[opt].isValid) {
|
|
305
|
+
this[opt] = options[opt];
|
|
306
|
+
} else if (options[opt] instanceof Date) {
|
|
307
|
+
this[opt] = luxon.DateTime.fromJSDate(options[opt]);
|
|
308
|
+
} else if (options[opt] === null) {
|
|
309
|
+
this[opt] = null;
|
|
310
|
+
} else {
|
|
311
|
+
console.error(`Option '${opt}' must be a luxon.DateTime or Date or string`);
|
|
312
|
+
}
|
|
313
|
+
} else if (typeof options[opt] === "string") {
|
|
314
|
+
const format = typeof this.locale.format === "string" ? this.locale.format : luxon.DateTime.parseFormatForOpts(this.locale.format);
|
|
315
|
+
if (luxon.DateTime.fromISO(options[opt]).isValid) {
|
|
316
|
+
this[opt] = luxon.DateTime.fromISO(options[opt]);
|
|
317
|
+
} else if (luxon.DateTime.fromFormat(options[opt], format, { locale: luxon.DateTime.now().locale }).isValid) {
|
|
318
|
+
this[opt] = luxon.DateTime.fromFormat(options[opt], format, { locale: luxon.DateTime.now().locale });
|
|
319
|
+
} else {
|
|
320
|
+
const invalid = luxon.DateTime.fromFormat(options[opt], format, { locale: luxon.DateTime.now().locale }).invalidExplanation;
|
|
321
|
+
console.error(`Option '${opt}' is not a valid string: ${invalid}`);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
if (this.isInputText) {
|
|
326
|
+
if (this.element.value != "") {
|
|
327
|
+
const format = typeof this.locale.format === "string" ? this.locale.format : luxon.DateTime.parseFormatForOpts(this.locale.format);
|
|
328
|
+
if (this.singleDatePicker && typeof options.startDate === "undefined") {
|
|
329
|
+
const start = luxon.DateTime.fromFormat(this.element.value, format, { locale: luxon.DateTime.now().locale });
|
|
330
|
+
if (start.isValid) {
|
|
331
|
+
this.#startDate = start;
|
|
332
|
+
} else {
|
|
333
|
+
console.error(`Value "${this.element.value}" in <input> is not a valid string: ${start.invalidExplanation}`);
|
|
334
|
+
}
|
|
335
|
+
} else if (!this.singleDatePicker && typeof options.startDate === "undefined" && typeof options.endDate === "undefined") {
|
|
336
|
+
const split = this.element.value.split(this.locale.separator);
|
|
337
|
+
if (split.length === 2) {
|
|
338
|
+
const start = luxon.DateTime.fromFormat(split[0], format, { locale: luxon.DateTime.now().locale });
|
|
339
|
+
const end = luxon.DateTime.fromFormat(split[1], format, { locale: luxon.DateTime.now().locale });
|
|
340
|
+
if (start.isValid && end.isValid) {
|
|
341
|
+
this.#startDate = start;
|
|
342
|
+
this.#endDate = end;
|
|
343
|
+
} else {
|
|
344
|
+
console.error(`Value in <input> is not a valid string: ${start.invalidExplanation} - ${end.invalidExplanation}`);
|
|
345
|
+
}
|
|
346
|
+
} else {
|
|
347
|
+
console.error(`Value "${this.element.value}" in <input> is not a valid string`);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
if (this.singleDatePicker) {
|
|
353
|
+
this.#endDate = this.#startDate;
|
|
354
|
+
} else if (this.#endDate < this.#startDate) {
|
|
355
|
+
console.error(`Option 'endDate' ${this.#endDate} must not be earlier than 'startDate' ${this.#startDate}`);
|
|
356
|
+
}
|
|
357
|
+
if (!this.timePicker) {
|
|
358
|
+
if (this.minDate) this.minDate = this.minDate.startOf("day");
|
|
359
|
+
if (this.maxDate) this.maxDate = this.maxDate.endOf("day");
|
|
360
|
+
this.#startDate = this.#startDate.startOf("day");
|
|
361
|
+
this.#endDate = this.#endDate.endOf("day");
|
|
362
|
+
}
|
|
363
|
+
if (!this.#startDate && this.initalMonth) {
|
|
364
|
+
this.#endDate = null;
|
|
365
|
+
if (this.timePicker)
|
|
366
|
+
console.error(`Option 'initalMonth' works only with 'timePicker: false'`);
|
|
367
|
+
} else {
|
|
368
|
+
const violations = this.validateInput(null, false);
|
|
369
|
+
if (violations != null) {
|
|
370
|
+
let vio = violations.startDate;
|
|
371
|
+
if (vio.length > 0) {
|
|
372
|
+
if (vio.some((x) => x.reason.startsWith("isInvalid"))) {
|
|
373
|
+
console.error(`Value of startDate "${this.#startDate}" violates ${vio.find((x) => x.reason.startsWith("isInvalid")).reason}`);
|
|
374
|
+
} else {
|
|
375
|
+
const newDate = vio.filter((x) => x.new != null).at(-1).new;
|
|
376
|
+
if (typeof process !== "undefined" && process.env.JEST_WORKER_ID == null)
|
|
377
|
+
console.warn(`Correcting startDate from ${this.#startDate} to ${newDate}`);
|
|
378
|
+
this.#startDate = newDate;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
if (!this.singleDatePicker) {
|
|
382
|
+
vio = violations.endDate.filter((x) => x.new != null);
|
|
383
|
+
if (vio.length > 0) {
|
|
384
|
+
if (vio.some((x) => x.reason.startsWith("isInvalid"))) {
|
|
385
|
+
console.error(`Value of endDate "${this.#endDate}" violates ${vio.find((x) => x.reason.startsWith("isInvalid")).reason}`);
|
|
386
|
+
} else {
|
|
387
|
+
const newDate = vio.filter((x) => x.new != null).at(-1).new;
|
|
388
|
+
if (typeof process !== "undefined" && process.env.JEST_WORKER_ID == null)
|
|
389
|
+
console.warn(`Correcting endDate from ${this.#endDate} to ${newDate}`);
|
|
390
|
+
this.#endDate = newDate;
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
if (this.singleDatePicker) {
|
|
397
|
+
if (typeof options.altInput === "string") {
|
|
398
|
+
const el = document.querySelector(options.altInput);
|
|
399
|
+
this.altInput = el instanceof HTMLInputElement && el.type === "text" ? el : null;
|
|
400
|
+
} else if (options.altInput instanceof HTMLElement) {
|
|
401
|
+
this.altInput = options.altInput instanceof HTMLInputElement && options.altInput.type === "text" ? options.altInput : null;
|
|
402
|
+
}
|
|
403
|
+
} else if (!this.singleDatePicker && Array.isArray(options.altInput) && options.altInput.length === 2) {
|
|
404
|
+
this.altInput = [];
|
|
405
|
+
for (let item of options.altInput) {
|
|
406
|
+
const el = typeof item === "string" ? document.querySelector(item) : item;
|
|
407
|
+
if (el instanceof HTMLInputElement && el.type === "text")
|
|
408
|
+
this.altInput.push(el);
|
|
409
|
+
}
|
|
410
|
+
if (this.altInput.length !== 2)
|
|
411
|
+
this.altInput = null;
|
|
412
|
+
} else if (options.altInput != null) {
|
|
413
|
+
console.warn(`Option 'altInput' ${JSON.stringify(options.altInput)} is not valid`);
|
|
414
|
+
}
|
|
415
|
+
if (options.altInput && ["function", "string"].includes(typeof options.altFormat))
|
|
416
|
+
this.altFormat = options.altFormat;
|
|
417
|
+
if (typeof options.opens === "string") {
|
|
418
|
+
if (["left", "right", "center"].includes(options.opens))
|
|
419
|
+
this.opens = options.opens;
|
|
420
|
+
else
|
|
421
|
+
console.error(`Option 'options.opens' must be 'left', 'right' or 'center'`);
|
|
422
|
+
}
|
|
423
|
+
if (typeof options.drops === "string") {
|
|
424
|
+
if (["up", "down", "auto"].includes(options.drops))
|
|
425
|
+
this.drops = options.drops;
|
|
426
|
+
else
|
|
427
|
+
console.error(`Option 'options.drops' must be 'up', 'down' or 'auto'`);
|
|
428
|
+
}
|
|
429
|
+
if (Array.isArray(options.buttonClasses)) {
|
|
430
|
+
this.buttonClasses = options.buttonClasses.join(" ");
|
|
431
|
+
} else if (typeof options.buttonClasses === "string") {
|
|
432
|
+
this.buttonClasses = options.buttonClasses;
|
|
433
|
+
}
|
|
434
|
+
if (typeof options.onOutsideClick === "string") {
|
|
435
|
+
if (["cancel", "apply"].includes(options.onOutsideClick))
|
|
436
|
+
this.onOutsideClick = options.onOutsideClick;
|
|
437
|
+
else
|
|
438
|
+
console.error(`Option 'options.onOutsideClick' must be 'cancel' or 'apply'`);
|
|
439
|
+
}
|
|
440
|
+
if (this.locale.firstDay != 1) {
|
|
441
|
+
let iterator = this.locale.firstDay;
|
|
442
|
+
while (iterator > 1) {
|
|
443
|
+
this.locale.daysOfWeek.push(this.locale.daysOfWeek.shift());
|
|
444
|
+
iterator--;
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
if (!this.singleDatePicker && typeof options.ranges === "object") {
|
|
448
|
+
for (let range in options.ranges) {
|
|
449
|
+
let start, end;
|
|
450
|
+
if (["string", "object"].includes(typeof options.ranges[range][0])) {
|
|
451
|
+
if (luxon.DateTime.isDateTime(options.ranges[range][0]) && options.ranges[range][0].isValid) {
|
|
452
|
+
start = options.ranges[range][0];
|
|
453
|
+
} else if (options.ranges[range][0] instanceof Date) {
|
|
454
|
+
start = luxon.DateTime.fromJSDate(options.ranges[range][0]);
|
|
455
|
+
} else if (typeof options.ranges[range][0] === "string" && luxon.DateTime.fromISO(options.ranges[range][0]).isValid) {
|
|
456
|
+
start = luxon.DateTime.fromISO(options.ranges[range][0]);
|
|
457
|
+
} else {
|
|
458
|
+
console.error(`Option ranges['${range}'] is not am array of valid ISO-8601 string or luxon.DateTime or Date`);
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
if (["string", "object"].includes(typeof options.ranges[range][1])) {
|
|
462
|
+
if (luxon.DateTime.isDateTime(options.ranges[range][1]) && options.ranges[range][1].isValid) {
|
|
463
|
+
end = options.ranges[range][1];
|
|
464
|
+
} else if (options.ranges[range][1] instanceof Date) {
|
|
465
|
+
end = luxon.DateTime.fromJSDate(options.ranges[range][1]);
|
|
466
|
+
} else if (typeof options.ranges[range][1] === "string" && luxon.DateTime.fromISO(options.ranges[range][1]).isValid) {
|
|
467
|
+
end = luxon.DateTime.fromISO(options.ranges[range][1]);
|
|
468
|
+
} else {
|
|
469
|
+
console.error(`Option ranges['${range}'] is not a valid ISO-8601 string or luxon.DateTime or Date`);
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
if (start == null || end == null)
|
|
473
|
+
continue;
|
|
474
|
+
var elem = document.createElement("textarea");
|
|
475
|
+
elem.innerHTML = range;
|
|
476
|
+
this.ranges[elem.value] = [start, end];
|
|
477
|
+
}
|
|
478
|
+
var list = "<ul>";
|
|
479
|
+
for (let range in this.ranges)
|
|
480
|
+
list += `<li data-range-key="${range}">${range}</li>`;
|
|
481
|
+
if (this.showCustomRangeLabel)
|
|
482
|
+
list += `<li data-range-key="${this.locale.customRangeLabel}">${this.locale.customRangeLabel}</li>`;
|
|
483
|
+
list += "</ul>";
|
|
484
|
+
this.container.querySelector(".ranges").prepend(createElementFromHTML(list));
|
|
485
|
+
this.container.classList.add("show-ranges");
|
|
486
|
+
}
|
|
487
|
+
if (typeof cb === "function")
|
|
488
|
+
this.callback = cb;
|
|
489
|
+
if (!this.timePicker)
|
|
490
|
+
this.container.querySelectorAll(".calendar-time").forEach((el) => {
|
|
491
|
+
el.style.display = "none";
|
|
492
|
+
});
|
|
493
|
+
if (this.timePicker && this.autoApply)
|
|
494
|
+
this.autoApply = false;
|
|
495
|
+
if (this.autoApply)
|
|
496
|
+
this.container.classList.add("auto-apply");
|
|
497
|
+
if (this.singleDatePicker || this.singleMonthView) {
|
|
498
|
+
this.container.classList.add("single");
|
|
499
|
+
this.container.querySelector(".drp-calendar.left").classList.add("single");
|
|
500
|
+
this.container.querySelector(".drp-calendar.left").style.display = "";
|
|
501
|
+
this.container.querySelector(".drp-calendar.right").style.display = "none";
|
|
502
|
+
if (!this.timePicker && this.autoApply)
|
|
503
|
+
this.container.classList.add("auto-apply");
|
|
504
|
+
}
|
|
505
|
+
if (this.singleDatePicker || !Object.keys(this.ranges).length || this.alwaysShowCalendars)
|
|
506
|
+
this.container.classList.add("show-calendar");
|
|
507
|
+
this.container.classList.add(`opens${this.opens}`);
|
|
508
|
+
this.container.querySelectorAll(".applyBtn, .cancelBtn").forEach((el) => {
|
|
509
|
+
el.classList.add(...this.buttonClasses.split(" "));
|
|
510
|
+
});
|
|
511
|
+
if (this.applyButtonClasses.length)
|
|
512
|
+
this.container.querySelector(".applyBtn").classList.add(...this.applyButtonClasses.split(" "));
|
|
513
|
+
if (this.cancelButtonClasses.length)
|
|
514
|
+
this.container.querySelector(".cancelBtn").classList.add(...this.cancelButtonClasses.split(" "));
|
|
515
|
+
this.container.querySelector(".applyBtn").innerHTML = this.locale.applyLabel;
|
|
516
|
+
this.container.querySelector(".cancelBtn").innerHTML = this.locale.cancelLabel;
|
|
517
|
+
this.addListener(".drp-calendar", "click", ".prev", this.clickPrev.bind(this));
|
|
518
|
+
this.addListener(".drp-calendar", "click", ".next", this.clickNext.bind(this));
|
|
519
|
+
this.addListener(".drp-calendar", "mousedown", "td.available", this.clickDate.bind(this));
|
|
520
|
+
this.addListener(".drp-calendar", "mouseenter", "td.available", this.hoverDate.bind(this));
|
|
521
|
+
this.addListener(".drp-calendar", "change", "select.yearselect,select.monthselect", this.monthOrYearChanged.bind(this));
|
|
522
|
+
this.addListener(".drp-calendar", "change", "select.hourselect,select.minuteselect,select.secondselect,select.ampmselect", this.timeChanged.bind(this));
|
|
523
|
+
this.addListener(".ranges", "click", "li", this.clickRange.bind(this));
|
|
524
|
+
this.addListener(".ranges", "mouseenter", "li", this.hoverRange.bind(this));
|
|
525
|
+
this.addListener(".ranges", "mouseleave", "li", this.leaveRange.bind(this));
|
|
526
|
+
this.addListener(".drp-buttons", "click", "button.applyBtn", this.clickApply.bind(this));
|
|
527
|
+
this.addListener(".drp-buttons", "click", "button.cancelBtn", this.clickCancel.bind(this));
|
|
528
|
+
if (this.element.matches("input") || this.element.matches("button")) {
|
|
529
|
+
this.element.addEventListener("click", this.#showProxy);
|
|
530
|
+
this.element.addEventListener("focus", this.#showProxy);
|
|
531
|
+
this.element.addEventListener("keyup", this.#elementChangedProxy);
|
|
532
|
+
this.element.addEventListener("keydown", this.#keydownProxy);
|
|
533
|
+
} else {
|
|
534
|
+
this.element.addEventListener("click", this.#toggleProxy);
|
|
535
|
+
this.element.addEventListener("keydown", this.#toggleProxy);
|
|
536
|
+
}
|
|
537
|
+
this.updateElement();
|
|
538
|
+
}
|
|
539
|
+
/**
|
|
540
|
+
* startDate
|
|
541
|
+
* @type {external:DateTime}
|
|
542
|
+
*/
|
|
543
|
+
get startDate() {
|
|
544
|
+
return this.timePicker ? this.#startDate : this.#startDate.startOf("day");
|
|
545
|
+
}
|
|
546
|
+
/**
|
|
547
|
+
* endDate
|
|
548
|
+
* @type {external:DateTime}
|
|
549
|
+
*/
|
|
550
|
+
get endDate() {
|
|
551
|
+
return this.singleDatePicker ? null : this.timePicker ? this.#endDate : this.#endDate.endOf("day");
|
|
552
|
+
}
|
|
553
|
+
set startDate(val) {
|
|
554
|
+
this.#startDate = val;
|
|
555
|
+
}
|
|
556
|
+
set endDate(val) {
|
|
557
|
+
this.#endDate = val;
|
|
558
|
+
}
|
|
559
|
+
/**
|
|
560
|
+
* DateRangePicker specific events
|
|
561
|
+
*/
|
|
562
|
+
#events = {
|
|
563
|
+
/**
|
|
564
|
+
* Emitted when the date is changed through `<input>` element or via {@link #DateRangePicker+setStartDate|setStartDate} or
|
|
565
|
+
* {@link #DateRangePicker+setRange|setRange} and date is not valid due to
|
|
566
|
+
* `minDate`, `maxDate`, `minSpan`, `maxSpan`, `invalidDate` and `invalidTime` constraints.<br>
|
|
567
|
+
* Event is only triggered when date string is valid and date value is changing<br>
|
|
568
|
+
* @event
|
|
569
|
+
* @name "violate"
|
|
570
|
+
* @property {DateRangePickerEvent} event - The Event object
|
|
571
|
+
* @property {DateRangePicker} event.picker - The daterangepicker object
|
|
572
|
+
* @property {InputViolation} event.violation - The daterangepicker object
|
|
573
|
+
* @property {NewDate} event.newDate - Object of corrected date values
|
|
574
|
+
* @property {boolean} event.cancelable=true - By calling `event.preventDefault()` the `newDate` values will apply
|
|
575
|
+
* @example
|
|
576
|
+
* daterangepicker('#picker', {
|
|
577
|
+
* startDate: DateTime.now(),
|
|
578
|
+
* // allow only dates from current year
|
|
579
|
+
* minDate: DateTime.now().startOf('year'),
|
|
580
|
+
* manDate: DateTime.now().endOf('year'),
|
|
581
|
+
* singleDatePicker: true,
|
|
582
|
+
* locale: {
|
|
583
|
+
* format: DateTime.DATETIME_SHORT
|
|
584
|
+
* }
|
|
585
|
+
* }).addEventListener('violate', (ev) => {
|
|
586
|
+
* ev.newDate.startDate = DateTime.now().minus({ days: 3 }).startOf('day');
|
|
587
|
+
* ev.preventDefault();
|
|
588
|
+
* });
|
|
589
|
+
*
|
|
590
|
+
* // Try to set date outside permitted range at <input> elemet
|
|
591
|
+
* const input = document.querySelector('#picker');
|
|
592
|
+
* input.value = DateTime.now().minus({ years: 10 })).toLocaleString(DateTime.DATETIME_SHORT)
|
|
593
|
+
* input.dispatchEvent(new Event('keyup'));
|
|
594
|
+
|
|
595
|
+
* // Try to set date outside permitted range by code
|
|
596
|
+
* const drp = getDateRangePicker('#picker');
|
|
597
|
+
* drp.setStartDate(DateTime.now().minus({ years: 10 });
|
|
598
|
+
*
|
|
599
|
+
* // -> Calendar selects and shows "today - 3 days"
|
|
600
|
+
*/
|
|
601
|
+
onViolate: { type: "violate", param: (violation, newDate) => {
|
|
602
|
+
return { ...violation, ...{ cancelable: true } };
|
|
603
|
+
} },
|
|
604
|
+
/**
|
|
605
|
+
* Emitted before the calendar time picker is rendered.
|
|
606
|
+
* @event
|
|
607
|
+
* @name "beforeRenderTimePicker"
|
|
608
|
+
* @property {DateRangePickerEvent} event - The Event object
|
|
609
|
+
* @property {DateRangePicker} event.picker - The daterangepicker object
|
|
610
|
+
*/
|
|
611
|
+
onBeforeRenderTimePicker: { type: "beforeRenderTimePicker" },
|
|
612
|
+
/**
|
|
613
|
+
* Emitted before the calendar is rendered.
|
|
614
|
+
* @event
|
|
615
|
+
* @name "beforeRenderCalendar"
|
|
616
|
+
* @property {DateRangePickerEvent} event - The Event object
|
|
617
|
+
* @property {DateRangePicker} event.picker - The daterangepicker object
|
|
618
|
+
*/
|
|
619
|
+
onBeforeRenderCalendar: { type: "beforeRenderCalendar" },
|
|
620
|
+
/**
|
|
621
|
+
* Emitted when the picker is shown
|
|
622
|
+
* @event
|
|
623
|
+
* @name "show"
|
|
624
|
+
* @property {DateRangePickerEvent} event - The Event object
|
|
625
|
+
* @property {DateRangePicker} event.picker - The daterangepicker object
|
|
626
|
+
*/
|
|
627
|
+
onShow: { type: "show" },
|
|
628
|
+
/**
|
|
629
|
+
* Emitted before the picker will hide.
|
|
630
|
+
* @event
|
|
631
|
+
* @name "beforeHide"
|
|
632
|
+
* @property {DateRangePickerEvent} event - The Event object
|
|
633
|
+
* @property {DateRangePicker} event.picker - The daterangepicker object
|
|
634
|
+
* @property {boolean} event.cancelable=true - Hide is canceled by calling `event.preventDefault()`
|
|
635
|
+
*/
|
|
636
|
+
onBeforeHide: { type: "beforeHide", param: { cancelable: true } },
|
|
637
|
+
/**
|
|
638
|
+
* Emitted when the picker is hidden
|
|
639
|
+
* @event
|
|
640
|
+
* @name "hide"
|
|
641
|
+
* @property {DateRangePickerEvent} event - The Event object
|
|
642
|
+
* @property {DateRangePicker} event.picker - The daterangepicker object
|
|
643
|
+
*/
|
|
644
|
+
onHide: { type: "hide" },
|
|
645
|
+
/**
|
|
646
|
+
* Emitted when the calendar(s) are shown.
|
|
647
|
+
* Only useful when {@link #Ranges|Ranges} are used.
|
|
648
|
+
* @event
|
|
649
|
+
* @name "showCalendar"
|
|
650
|
+
* @property {DateRangePickerEvent} event - The Event object
|
|
651
|
+
* @property {DateRangePicker} event.picker - The daterangepicker object
|
|
652
|
+
*/
|
|
653
|
+
onShowCalendar: { type: "showCalendar" },
|
|
654
|
+
/**
|
|
655
|
+
* Emitted when the calendar(s) are hidden. Only used when {@link #Ranges|Ranges} are used.
|
|
656
|
+
* @event
|
|
657
|
+
* @name "hideCalendar"
|
|
658
|
+
* @property {DateRangePickerEvent} event - The Event object
|
|
659
|
+
* @property {DateRangePicker} event.picker - The daterangepicker object
|
|
660
|
+
*/
|
|
661
|
+
onHideCalendar: { type: "hideCalendar" },
|
|
662
|
+
/**
|
|
663
|
+
* Emitted when user clicks outside the picker. Use option `onOutsideClick` to define the default action, then you may not need to handle this event.
|
|
664
|
+
* @event
|
|
665
|
+
* @name "outsideClick"
|
|
666
|
+
* @property {DateRangePickerEvent} event - The Event object
|
|
667
|
+
* @property {DateRangePicker} event.picker - The daterangepicker object
|
|
668
|
+
* @property {boolean} event.cancelable=true - Call `event.preventDefault()` to prevent default behaviour.<br>
|
|
669
|
+
* Useful to define custome areas where click shall not hide the picker
|
|
670
|
+
*/
|
|
671
|
+
onOutsideClick: { type: "outsideClick", param: { cancelable: true } },
|
|
672
|
+
/**
|
|
673
|
+
* Emitted when the date changed. Does not trigger when time is changed, use {@link #event_timeChange|"timeChange"} to handle it
|
|
674
|
+
* @event
|
|
675
|
+
* @name "dateChange"
|
|
676
|
+
* @property {DateRangePickerEvent} event - The Event object
|
|
677
|
+
* @property {DateRangePicker} event.picker - The daterangepicker object
|
|
678
|
+
* @property {string} event.side - Either `'start'` or `'end'` indicating whether `startDate` or `endDate` was changed. `null` for singleDatePicker
|
|
679
|
+
*/
|
|
680
|
+
onDateChange: { type: "dateChange", param: (side) => {
|
|
681
|
+
return side;
|
|
682
|
+
} },
|
|
683
|
+
/**
|
|
684
|
+
* Emitted when the time changed. Does not trigger when date is changed
|
|
685
|
+
* @event
|
|
686
|
+
* @name "timeChange"
|
|
687
|
+
* @property {DateRangePickerEvent} event - The Event object
|
|
688
|
+
* @property {DateRangePicker} event.picker - The daterangepicker object
|
|
689
|
+
* @property {string} event.side - Either `'start'` or `'end'` indicating whether `startDate` or `endDate` was changed. `null` for singleDatePicker
|
|
690
|
+
*/
|
|
691
|
+
onTimeChange: { type: "timeChange", param: (side) => {
|
|
692
|
+
return side;
|
|
693
|
+
} },
|
|
694
|
+
/**
|
|
695
|
+
* Emitted when the `Apply` button is clicked, or when a predefined {@link #Ranges|Ranges} is clicked
|
|
696
|
+
* @event
|
|
697
|
+
* @name "apply"
|
|
698
|
+
* @property {DateRangePickerEvent} event - The Event object
|
|
699
|
+
* @property {DateRangePicker} event.picker - The daterangepicker object
|
|
700
|
+
*/
|
|
701
|
+
onApply: { type: "apply" },
|
|
702
|
+
/**
|
|
703
|
+
* Emitted when the `Cancel` button is clicked
|
|
704
|
+
* @event
|
|
705
|
+
* @name "cancel"
|
|
706
|
+
* @property {DateRangePickerEvent} event - The Event object
|
|
707
|
+
* @property {DateRangePicker} event.picker - The daterangepicker object
|
|
708
|
+
*/
|
|
709
|
+
onCancel: { type: "cancel" },
|
|
710
|
+
/**
|
|
711
|
+
* Emitted when the date is changed through `<input>` element. Event is only triggered when date string is valid and date value has changed
|
|
712
|
+
* @event
|
|
713
|
+
* @name "inputChange"
|
|
714
|
+
* @property {DateRangePickerEvent} event - The Event object
|
|
715
|
+
* @property {DateRangePicker} event.picker - The daterangepicker object
|
|
716
|
+
*/
|
|
717
|
+
onInputChange: { type: "inputChange" },
|
|
718
|
+
/**
|
|
719
|
+
* Emitted after month view changed, for example by click on 'prev' or 'next'
|
|
720
|
+
* @event
|
|
721
|
+
* @name "monthViewChange"
|
|
722
|
+
* @property {DateRangePickerEvent} event - The Event object
|
|
723
|
+
* @property {DateRangePicker} event.picker - The daterangepicker object
|
|
724
|
+
* @property {external:DateTime} event.left - The first day of month in left-hand calendar
|
|
725
|
+
* @property {external:DateTime} event.right - The first day of month in left-hand calendar or `null` for singleDatePicker
|
|
726
|
+
*/
|
|
727
|
+
onMonthViewChange: {
|
|
728
|
+
type: "monthViewChange",
|
|
729
|
+
param: (left, right) => {
|
|
730
|
+
return {
|
|
731
|
+
left: this.leftCalendar.month.startOf("month"),
|
|
732
|
+
right: this.singleMonthView || this.singleDatePicker ? null : this.rightCalendar.month.startOf("month")
|
|
733
|
+
};
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
};
|
|
737
|
+
/**
|
|
738
|
+
* Getter for all DateRangePickerEvents
|
|
739
|
+
*/
|
|
740
|
+
get events() {
|
|
741
|
+
return this.#events;
|
|
742
|
+
}
|
|
743
|
+
#outsideClickProxy = this.outsideClick.bind(this);
|
|
744
|
+
#onResizeProxy = this.move.bind(this);
|
|
745
|
+
#dropdownClickWrapper = (e) => {
|
|
746
|
+
const match = e.target.closest('[data-toggle="dropdown"]');
|
|
747
|
+
if (match && document.contains(match))
|
|
748
|
+
this.#outsideClickProxy(e);
|
|
749
|
+
};
|
|
750
|
+
#showProxy = this.show.bind(this);
|
|
751
|
+
#elementChangedProxy = this.elementChanged.bind(this);
|
|
752
|
+
#keydownProxy = this.keydown.bind(this);
|
|
753
|
+
#toggleProxy = this.toggle.bind(this);
|
|
754
|
+
/* #region Set startDate/endDate */
|
|
755
|
+
/**
|
|
756
|
+
* Sets the date range picker's currently selected start date to the provided date.<br>
|
|
757
|
+
* `startDate` must be a `luxon.DateTime` or `Date` or `string` according to {@link ISO-8601} or a string matching `locale.format`.<br>
|
|
758
|
+
* Invalid date values are handled by {@link #DateRangePicker+violate|violate} Event
|
|
759
|
+
* @param {external:DateTime|external:Date|string} startDate - startDate to be set. In case of ranges, the current `endDate` is used.
|
|
760
|
+
* @param {boolean} updateView=true - If `true`, then calendar UI is updated to new value. Otherwise only internal values are set.
|
|
761
|
+
* @returns {InputViolation} - Object of violations or `null` if no violation have been found
|
|
762
|
+
* @example
|
|
763
|
+
* const drp = getDateRangePicker('#picker');
|
|
764
|
+
* drp.setStartDate(DateTime.now().startOf('hour'));
|
|
765
|
+
*/
|
|
766
|
+
setStartDate(startDate, updateView = true) {
|
|
767
|
+
if (!this.singleDatePicker)
|
|
768
|
+
return setRange(startDate, this.#endDate, updateView);
|
|
769
|
+
const oldDate = this.#startDate;
|
|
770
|
+
let newDate = this.parseDate(startDate);
|
|
771
|
+
if (newDate.equals(oldDate))
|
|
772
|
+
return null;
|
|
773
|
+
const violations = this.validateInput([newDate, null], true);
|
|
774
|
+
if (violations != null) {
|
|
775
|
+
if (violations.newDate != null) {
|
|
776
|
+
newDate = violations.newDate.startDate;
|
|
777
|
+
} else {
|
|
778
|
+
return violations;
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
const monthChange = !this.#startDate.hasSame(newDate, "month");
|
|
782
|
+
this.#startDate = newDate;
|
|
783
|
+
this.#endDate = this.#startDate;
|
|
784
|
+
if (!this.timePicker) {
|
|
785
|
+
this.#startDate = this.#startDate.startOf("day");
|
|
786
|
+
this.#endDate = this.#endDate.endOf("day");
|
|
787
|
+
}
|
|
788
|
+
this.updateElement();
|
|
789
|
+
if (updateView)
|
|
790
|
+
this.updateView(monthChange);
|
|
791
|
+
return violations;
|
|
792
|
+
}
|
|
793
|
+
/**
|
|
794
|
+
* Sets the date range picker's currently selected start date to the provided date.<br>
|
|
795
|
+
* `endDate` must be a `luxon.DateTime` or `Date` or `string` according to {@link ISO-8601} or a string matching `locale.format`.<br>
|
|
796
|
+
* Invalid date values are handled by {@link #DateRangePicker+violate|violate} Event
|
|
797
|
+
* @param {external:DateTime|external:Date|string} endDate - endDate to be set. In case of ranges, the current `startDate` is used.
|
|
798
|
+
* @param {boolean} updateView=true - If `true`, then calendar UI is updated to new value. Otherwise only internal values are set.
|
|
799
|
+
* @returns {InputViolation} - Object of violations or `null` if no violation have been found
|
|
800
|
+
* @example
|
|
801
|
+
* const drp = getDateRangePicker('#picker');
|
|
802
|
+
* drp.setEndDate(DateTime.now().startOf('hour'));
|
|
803
|
+
*/
|
|
804
|
+
setEndDate(endDate, updateView = true) {
|
|
805
|
+
return this.singleDatePicker ? null : setRange(this.#startDate, endDate, updateView);
|
|
806
|
+
}
|
|
807
|
+
/**
|
|
808
|
+
* Sets the date range picker's currently selected start date to the provided date.<br>
|
|
809
|
+
* `startDate` and `endDate` must be a `luxon.DateTime` or `Date` or `string` according to {@link ISO-8601} or a string matching `locale.format`.<br>
|
|
810
|
+
* Invalid date values are handled by {@link #DateRangePicker+violate|violate} Event
|
|
811
|
+
* @param {external:DateTime|external:Date|string} startDate - startDate to be set
|
|
812
|
+
* @param {external:DateTime|external:Date|string} endDate - endDate to be set
|
|
813
|
+
* @param {boolean} updateView=true - If `true`, then calendar UI is updated to new value. Otherwise only internal values are set.
|
|
814
|
+
* @returns {InputViolation} - Object of violations or `null` if no violation have been found
|
|
815
|
+
* @example
|
|
816
|
+
* const drp = getDateRangePicker('#picker');
|
|
817
|
+
* drp.setRange(DateTime.now().startOf('hour'), DateTime.now().endOf('day'));
|
|
818
|
+
*/
|
|
819
|
+
setRange(startDate, endDate, updateView = true) {
|
|
820
|
+
if (this.singleDatePicker)
|
|
821
|
+
return;
|
|
822
|
+
if (!this.#endDate)
|
|
823
|
+
this.#endDate = this.#startDate;
|
|
824
|
+
const oldDate = [this.#startDate, this.#endDate];
|
|
825
|
+
let newDate = [this.parseDate(startDate), this.parseDate(endDate)];
|
|
826
|
+
if (oldDate[0].equals(newDate[0]) && oldDate[1].equals(newDate[1]) || newDate[0] > newDate[1])
|
|
827
|
+
return;
|
|
828
|
+
const violations = this.validateInput([newDate[0], newDate[1]], true);
|
|
829
|
+
if (violations != null) {
|
|
830
|
+
if (violations.newDate != null) {
|
|
831
|
+
newDate[0] = violations.newDate.startDate;
|
|
832
|
+
newDate[1] = violations.newDate.endDate;
|
|
833
|
+
} else {
|
|
834
|
+
return violations;
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
const monthChange = !this.#startDate.hasSame(newDate[0], "month") || !this.#endDate.hasSame(newDate[1], "month");
|
|
838
|
+
this.#startDate = newDate[0];
|
|
839
|
+
this.#endDate = newDate[1];
|
|
840
|
+
if (!this.timePicker) {
|
|
841
|
+
this.#startDate = this.#startDate.startOf("day");
|
|
842
|
+
this.#endDate = this.#endDate.endOf("day");
|
|
843
|
+
}
|
|
844
|
+
this.updateElement();
|
|
845
|
+
if (updateView)
|
|
846
|
+
this.updateView(monthChange);
|
|
847
|
+
return violations;
|
|
848
|
+
}
|
|
849
|
+
/**
|
|
850
|
+
* Parse date value
|
|
851
|
+
* @param {sting|external:DateTime|Date} value - The value to be parsed
|
|
852
|
+
* @returns {external:DateTime} - DateTime object
|
|
853
|
+
*/
|
|
854
|
+
parseDate(value) {
|
|
855
|
+
if (typeof value === "object") {
|
|
856
|
+
if (luxon.DateTime.isDateTime(value) && value.isValid) {
|
|
857
|
+
return value;
|
|
858
|
+
} else if (value instanceof Date) {
|
|
859
|
+
return luxon.DateTime.fromJSDate(value);
|
|
860
|
+
} else {
|
|
861
|
+
throw RangeError(`Value must be a luxon.DateTime or Date or string`);
|
|
862
|
+
}
|
|
863
|
+
} else if (typeof value === "string") {
|
|
864
|
+
const format = typeof this.locale.format === "string" ? this.locale.format : luxon.DateTime.parseFormatForOpts(this.locale.format);
|
|
865
|
+
if (luxon.DateTime.fromISO(value).isValid) {
|
|
866
|
+
return luxon.DateTime.fromISO(value);
|
|
867
|
+
} else if (luxon.DateTime.fromFormat(value, format, { locale: luxon.DateTime.now().locale }).isValid) {
|
|
868
|
+
return luxon.DateTime.fromFormat(value, format, { locale: luxon.DateTime.now().locale });
|
|
869
|
+
} else {
|
|
870
|
+
const invalid = luxon.DateTime.fromFormat(value, format, { locale: luxon.DateTime.now().locale }).invalidExplanation;
|
|
871
|
+
throw RangeError(`Value is not a valid string: ${invalid}`);
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
/* #endregion */
|
|
876
|
+
/**
|
|
877
|
+
* Format a DateTime object
|
|
878
|
+
* @param {external:DateTime} date - The DateTime to format
|
|
879
|
+
* @param {object|string} format=this.locale.format - The format option
|
|
880
|
+
* @returns {string} - Formatted date string
|
|
881
|
+
*/
|
|
882
|
+
formatDate(date, format = this.locale.format) {
|
|
883
|
+
if (typeof format === "object") {
|
|
884
|
+
return date.toLocaleString(format);
|
|
885
|
+
} else {
|
|
886
|
+
if (luxon.Settings.defaultLocale === null) {
|
|
887
|
+
const locale = luxon.DateTime.now().locale;
|
|
888
|
+
return date.toFormat(format, { locale });
|
|
889
|
+
} else {
|
|
890
|
+
return date.toFormat(format);
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
/**
|
|
895
|
+
* Set Duration Label to selected range (if used) and selected dates
|
|
896
|
+
* @private
|
|
897
|
+
*/
|
|
898
|
+
updateLabel() {
|
|
899
|
+
if (this.showLabel) {
|
|
900
|
+
let text = this.formatDate(this.#startDate);
|
|
901
|
+
if (!this.singleDatePicker) {
|
|
902
|
+
text += this.locale.separator;
|
|
903
|
+
if (this.#endDate)
|
|
904
|
+
text += this.formatDate(this.#endDate);
|
|
905
|
+
}
|
|
906
|
+
this.container.querySelector(".drp-selected").innerHTML = text;
|
|
907
|
+
}
|
|
908
|
+
if (this.singleDatePicker || this.locale.durationFormat == null)
|
|
909
|
+
return;
|
|
910
|
+
if (!this.#endDate) {
|
|
911
|
+
this.container.querySelector(".drp-duration-label").innerHTML = "";
|
|
912
|
+
return;
|
|
913
|
+
}
|
|
914
|
+
if (typeof this.locale.durationFormat === "function") {
|
|
915
|
+
this.container.querySelector(".drp-duration-label").innerHTML = this.locale.durationFormat(this.#startDate, this.#endDate);
|
|
916
|
+
} else {
|
|
917
|
+
let duration = this.#endDate.plus({ milliseconds: 1 }).diff(this.#startDate).rescale().set({ milliseconds: 0 });
|
|
918
|
+
if (!this.timePicker)
|
|
919
|
+
duration = duration.set({ seconds: 0, minutes: 0, hours: 0 });
|
|
920
|
+
duration = duration.removeZeros();
|
|
921
|
+
if (typeof this.locale.durationFormat === "object") {
|
|
922
|
+
this.container.querySelector(".drp-duration-label").innerHTML = duration.toHuman(this.locale.durationFormat);
|
|
923
|
+
} else {
|
|
924
|
+
this.container.querySelector(".drp-duration-label").innerHTML = duration.toFormat(this.locale.durationFormat);
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
}
|
|
928
|
+
/**
|
|
929
|
+
* @typedef InputViolation
|
|
930
|
+
* @type {Object}
|
|
931
|
+
* @typedef {object} Violation
|
|
932
|
+
* @property {string} reason - The type/reason of violation
|
|
933
|
+
* @property {external:DateTime} old - Old value startDate/endDate
|
|
934
|
+
* @property {external:DateTime} new? - Corrected value of startDate/endDate if existing
|
|
935
|
+
* @typedef {object} NewDate
|
|
936
|
+
* @property {external:DateTime} newDate.startDate- Object with corrected values
|
|
937
|
+
* @property {external:DateTime} newDate.endDate - Object with corrected values
|
|
938
|
+
* @property {Violation[]} startDate - The constraints which violates the input
|
|
939
|
+
* @property {Violation[]?} endDate - The constraints which violates the input or `null` for singleDatePicker
|
|
940
|
+
* @property {NewDate} newDate - Object with corrected values
|
|
941
|
+
*/
|
|
942
|
+
/**
|
|
943
|
+
* Validate `startDate` and `endDate` against `timePickerStepSize`, `minDate`, `maxDate`,
|
|
944
|
+
* `minSpan`, `maxSpan`, `invalidDate` and `invalidTime`.
|
|
945
|
+
* @param {Array} range - `[startDate, endDate]`<br>Range to be checked, defaults to current `startDate` and `endDate`
|
|
946
|
+
* @param {boolean} dipatch=false - If `true` then event "violate" is dispated.<br>
|
|
947
|
+
* If eventHandler returns `true`, then `null` is returned, otherwiese the object of violations.
|
|
948
|
+
* @emits "violate"
|
|
949
|
+
* @returns {InputViolation|null} - Object of violations and corrected values or `null` if no violation have been found
|
|
950
|
+
* @example
|
|
951
|
+
* options => {
|
|
952
|
+
* minDate: DateTime.now().minus({months: 3}).startOf('day'),
|
|
953
|
+
* maxDate: DateTime.now().minus({day: 3}).startOf('day'),
|
|
954
|
+
* minSpan: Duration.fromObject({days: 7}),
|
|
955
|
+
* maxSpan: Duration.fromObject({days: 70}),
|
|
956
|
+
* timePickerStepSize: Duration.fromObject({hours: 1})
|
|
957
|
+
* }
|
|
958
|
+
* const result = validateInput(DateTime.now(), DateTime.now().plus({day: 3}));
|
|
959
|
+
*
|
|
960
|
+
* result => {
|
|
961
|
+
* startDate: [
|
|
962
|
+
* { old: "2026-03-13T10:35:52", reason: "timePickerStepSize", new: "2026-03-13T11:00:00" },
|
|
963
|
+
* { old: "2026-03-13T11:00:00", reason: "maxDate", new: "2026-03-10T00:00:00" }
|
|
964
|
+
* ],
|
|
965
|
+
* endDate: {
|
|
966
|
+
* { old: "2026-03-16T10:35:52", reason: "stepSize", new: "2026-03-16T11:00:00" },
|
|
967
|
+
* { old: "2026-03-16T11:00:00", reason: "maxDate", new: "2026-03-10T00:00:00" },
|
|
968
|
+
* { old: "2026-03-10T00:00:00", reason: "minSpan", new: "2026-03-17T00:00:00" }
|
|
969
|
+
* ],
|
|
970
|
+
* newDate: {
|
|
971
|
+
* startDate: "2026-03-10T00:00:00",
|
|
972
|
+
* endDate: "2026-03-17T00:00:00"
|
|
973
|
+
* }
|
|
974
|
+
* }
|
|
975
|
+
*/
|
|
976
|
+
validateInput(range, dipatch = false) {
|
|
977
|
+
let startDate = range == null ? this.#startDate : range[0];
|
|
978
|
+
let endDate = range == null ? this.#endDate : range[1];
|
|
979
|
+
if (startDate == null)
|
|
980
|
+
return null;
|
|
981
|
+
let result = { startDate: [] };
|
|
982
|
+
let violation = { old: startDate, reason: this.timePicker ? "timePickerStepSize" : "timePicker" };
|
|
983
|
+
if (this.timePicker) {
|
|
984
|
+
const secs = this.timePickerStepSize.as("seconds");
|
|
985
|
+
startDate = luxon.DateTime.fromSeconds(secs * Math.round(startDate.toSeconds() / secs));
|
|
986
|
+
violation.new = startDate;
|
|
987
|
+
if (!violation.new.equals(violation.old))
|
|
988
|
+
result.startDate.push(violation);
|
|
989
|
+
} else {
|
|
990
|
+
startDate = startDate.startOf("day");
|
|
991
|
+
}
|
|
992
|
+
const shiftStep = this.timePicker ? this.timePickerStepSize.as("seconds") : luxon.Duration.fromObject({ days: 1 }).as("seconds");
|
|
993
|
+
if (this.minDate && startDate < this.minDate) {
|
|
994
|
+
violation = { old: startDate, reason: "minDate" };
|
|
995
|
+
startDate = startDate.plus({ seconds: Math.trunc(this.minDate.diff(startDate).as("seconds") / shiftStep) * shiftStep });
|
|
996
|
+
if (startDate < this.minDate)
|
|
997
|
+
startDate = startDate.plus(this.timePicker ? this.timePickerStepSize : { days: 1 });
|
|
998
|
+
violation.new = startDate;
|
|
999
|
+
if (!violation.new.equals(violation.old))
|
|
1000
|
+
result.startDate.push(violation);
|
|
1001
|
+
} else if (this.maxDate && startDate > this.maxDate) {
|
|
1002
|
+
violation = { old: startDate, reason: "maxDate" };
|
|
1003
|
+
startDate = startDate.minus({ seconds: Math.trunc(startDate.diff(this.maxDate).as("seconds") / shiftStep) * shiftStep });
|
|
1004
|
+
if (startDate > this.maxDate)
|
|
1005
|
+
startDate = startDate.minus(this.timePicker ? this.timePickerStepSize : { days: 1 });
|
|
1006
|
+
violation.new = startDate;
|
|
1007
|
+
if (!violation.new.equals(violation.old))
|
|
1008
|
+
result.startDate.push(violation);
|
|
1009
|
+
}
|
|
1010
|
+
let units = ["hour"];
|
|
1011
|
+
if (this.timePicker) {
|
|
1012
|
+
if (this.timePickerOpts.showMinutes)
|
|
1013
|
+
units.push("minute");
|
|
1014
|
+
if (this.timePickerOpts.showSeconds)
|
|
1015
|
+
units.push("second");
|
|
1016
|
+
if (!this.timePicker24Hour)
|
|
1017
|
+
units.push("ampm");
|
|
1018
|
+
}
|
|
1019
|
+
if (this.isInvalidDate(startDate))
|
|
1020
|
+
result.startDate.push({ old: startDate, reason: "isInvalidDate" });
|
|
1021
|
+
if (this.timePicker) {
|
|
1022
|
+
for (let unit of units) {
|
|
1023
|
+
if (this.isInvalidTime(startDate, unit, "start"))
|
|
1024
|
+
result.startDate.push({ old: startDate, reason: "isInvalidTime", unit });
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
if (this.singleDatePicker) {
|
|
1028
|
+
if (result.startDate.length == 0)
|
|
1029
|
+
return null;
|
|
1030
|
+
if (dipatch) {
|
|
1031
|
+
let newValues = { startDate };
|
|
1032
|
+
const event = this.triggerEvent(this.#events.onViolate, { violation: result, newDate: newValues });
|
|
1033
|
+
if (event.defaultPrevented) {
|
|
1034
|
+
result.newDate = event.newDate;
|
|
1035
|
+
return result;
|
|
1036
|
+
}
|
|
1037
|
+
return result;
|
|
1038
|
+
} else {
|
|
1039
|
+
return result;
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
1042
|
+
if (endDate == null)
|
|
1043
|
+
return null;
|
|
1044
|
+
result.endDate = [];
|
|
1045
|
+
violation = { old: endDate, reason: this.timePicker ? "stepSize" : "timePicker" };
|
|
1046
|
+
if (this.timePicker) {
|
|
1047
|
+
const secs = this.timePickerStepSize.as("seconds");
|
|
1048
|
+
endDate = luxon.DateTime.fromSeconds(secs * Math.round(endDate.toSeconds() / secs));
|
|
1049
|
+
violation.new = endDate;
|
|
1050
|
+
if (!violation.new.equals(violation.old))
|
|
1051
|
+
result.endDate.push(violation);
|
|
1052
|
+
} else {
|
|
1053
|
+
endDate = endDate.endOf("day");
|
|
1054
|
+
}
|
|
1055
|
+
if (this.maxDate && endDate > this.maxDate) {
|
|
1056
|
+
violation = { old: endDate, reason: "maxDate" };
|
|
1057
|
+
endDate = endDate.minus({ seconds: Math.trunc(endDate.diff(this.maxDate).as("seconds") / shiftStep) * shiftStep });
|
|
1058
|
+
if (endDate > this.maxDate)
|
|
1059
|
+
endDate = endDate.minus(this.timePicker ? this.timePickerStepSize : { days: 1 });
|
|
1060
|
+
violation.new = endDate;
|
|
1061
|
+
if (!violation.new.equals(violation.old))
|
|
1062
|
+
result.endDate.push(violation);
|
|
1063
|
+
} else if (this.minDate && endDate < this.minDate) {
|
|
1064
|
+
violation = { old: endDate, reason: "minDate" };
|
|
1065
|
+
endDate = endDate.plus({ seconds: Math.trunc(this.minDate.diff(endDate).as("seconds") / shiftStep) * shiftStep });
|
|
1066
|
+
if (endDate < this.minDate)
|
|
1067
|
+
endDate = endDate.plus(this.timePicker ? this.timePickerStepSize : { days: 1 });
|
|
1068
|
+
violation.new = endDate;
|
|
1069
|
+
if (!violation.new.equals(violation.old))
|
|
1070
|
+
result.endDate.push(violation);
|
|
1071
|
+
}
|
|
1072
|
+
if (this.maxSpan) {
|
|
1073
|
+
const maxDate = startDate.plus(this.maxSpan);
|
|
1074
|
+
if (endDate > maxDate) {
|
|
1075
|
+
violation = { old: endDate, reason: "maxSpan" };
|
|
1076
|
+
endDate = endDate.minus({ seconds: Math.trunc(maxDate.diff(endDate).as("seconds") / shiftStep) * shiftStep });
|
|
1077
|
+
if (endDate > maxDate)
|
|
1078
|
+
endDate = endDate.minus(this.timePicker ? this.timePickerStepSize : { days: 1 });
|
|
1079
|
+
violation.new = endDate;
|
|
1080
|
+
if (!violation.new.equals(violation.old))
|
|
1081
|
+
result.endDate.push(violation);
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1084
|
+
if (this.minSpan) {
|
|
1085
|
+
const minDate = startDate.plus(this.defaultSpan ?? this.minSpan);
|
|
1086
|
+
if (endDate < minDate) {
|
|
1087
|
+
violation = { old: endDate, reason: "minSpan" };
|
|
1088
|
+
endDate = endDate.plus({ seconds: Math.trunc(minDate.diff(endDate).as("seconds") / shiftStep) * shiftStep });
|
|
1089
|
+
if (endDate < minDate)
|
|
1090
|
+
endDate = endDate.plus(this.timePicker ? this.timePickerStepSize : { days: 1 });
|
|
1091
|
+
violation.new = endDate;
|
|
1092
|
+
if (!violation.new.equals(violation.old))
|
|
1093
|
+
result.endDate.push(violation);
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
if (this.isInvalidDate(endDate))
|
|
1097
|
+
result.endDate.push({ old: endDate, reason: "isInvalidDate" });
|
|
1098
|
+
if (this.timePicker) {
|
|
1099
|
+
for (let unit of units) {
|
|
1100
|
+
if (this.isInvalidTime(endDate, unit, "end"))
|
|
1101
|
+
result.endDate.push({ old: endDate, reason: "isInvalidTime", unit });
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
1104
|
+
if (result.startDate.length == 0 && result.endDate.length == 0)
|
|
1105
|
+
return null;
|
|
1106
|
+
if (dipatch) {
|
|
1107
|
+
let newValues = { startDate, endDate };
|
|
1108
|
+
const event = this.triggerEvent(this.#events.onViolate, { violation: result, newDate: newValues });
|
|
1109
|
+
if (event.defaultPrevented) {
|
|
1110
|
+
result.newDate = event.newDate;
|
|
1111
|
+
return result;
|
|
1112
|
+
}
|
|
1113
|
+
return result;
|
|
1114
|
+
} else {
|
|
1115
|
+
return result;
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
/* #region Rendering */
|
|
1119
|
+
/**
|
|
1120
|
+
* Updates the picker when calendar is initiated or any date has been selected.
|
|
1121
|
+
* Could be useful after running {@link #DateRangePicker+setStartDate|setStartDate} or {@link #DateRangePicker+setEndDate|setRange}
|
|
1122
|
+
* @param {boolean} monthChange - If `true` then monthView changed
|
|
1123
|
+
* @emits "beforeRenderTimePicker"
|
|
1124
|
+
*/
|
|
1125
|
+
updateView(monthChange) {
|
|
1126
|
+
if (this.timePicker) {
|
|
1127
|
+
this.triggerEvent(this.#events.onBeforeRenderTimePicker);
|
|
1128
|
+
this.renderTimePicker("start");
|
|
1129
|
+
this.renderTimePicker("end");
|
|
1130
|
+
this.container.querySelector(".calendar-time.end-time select").disabled = !this.#endDate;
|
|
1131
|
+
this.container.querySelector(".calendar-time.end-time select").classList.toggle("disabled", !this.#endDate);
|
|
1132
|
+
}
|
|
1133
|
+
this.updateLabel();
|
|
1134
|
+
this.updateMonthsInView();
|
|
1135
|
+
this.updateCalendars(monthChange);
|
|
1136
|
+
this.setApplyBtnState();
|
|
1137
|
+
}
|
|
1138
|
+
/**
|
|
1139
|
+
* Shows calendar months based on selected date values
|
|
1140
|
+
* @private
|
|
1141
|
+
*/
|
|
1142
|
+
updateMonthsInView() {
|
|
1143
|
+
if (this.#endDate) {
|
|
1144
|
+
if (!this.singleDatePicker && this.leftCalendar.month && this.rightCalendar.month && (this.#startDate.hasSame(this.leftCalendar.month, "month") || this.#startDate.hasSame(this.rightCalendar.month, "month")) && (this.#endDate.hasSame(this.leftCalendar.month, "month") || this.#endDate.hasSame(this.rightCalendar.month, "month")))
|
|
1145
|
+
return;
|
|
1146
|
+
this.leftCalendar.month = this.#startDate.startOf("month");
|
|
1147
|
+
if (!this.singleMonthView) {
|
|
1148
|
+
if (!this.linkedCalendars && !this.#endDate.hasSame(this.#startDate, "month")) {
|
|
1149
|
+
this.rightCalendar.month = this.#endDate.startOf("month");
|
|
1150
|
+
} else {
|
|
1151
|
+
this.rightCalendar.month = this.#startDate.startOf("month").plus({ month: 1 });
|
|
1152
|
+
}
|
|
1153
|
+
}
|
|
1154
|
+
} else {
|
|
1155
|
+
if (!this.#startDate && this.initalMonth) {
|
|
1156
|
+
this.leftCalendar.month = this.initalMonth;
|
|
1157
|
+
if (!this.singleMonthView)
|
|
1158
|
+
this.rightCalendar.month = this.initalMonth.plus({ month: 1 });
|
|
1159
|
+
} else {
|
|
1160
|
+
if (!this.leftCalendar.month.hasSame(this.#startDate, "month") && !this.rightCalendar.month.hasSame(this.#startDate, "month")) {
|
|
1161
|
+
this.leftCalendar.month = this.#startDate.startOf("month");
|
|
1162
|
+
this.rightCalendar.month = this.#startDate.startOf("month").plus({ month: 1 });
|
|
1163
|
+
}
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1166
|
+
if (this.maxDate && this.linkedCalendars && !this.singleDatePicker && !this.singleMonthView && this.rightCalendar.month > this.maxDate) {
|
|
1167
|
+
this.rightCalendar.month = this.maxDate.startOf("month");
|
|
1168
|
+
this.leftCalendar.month = this.maxDate.startOf("month").minus({ month: 1 });
|
|
1169
|
+
}
|
|
1170
|
+
}
|
|
1171
|
+
/**
|
|
1172
|
+
* Updates the selected day value from calendar with selected time values
|
|
1173
|
+
* @emits "beforeRenderCalendar"
|
|
1174
|
+
* @emits "monthViewChange"
|
|
1175
|
+
* @param {boolean} monthChange - If `true` then monthView changed
|
|
1176
|
+
* @private
|
|
1177
|
+
*/
|
|
1178
|
+
updateCalendars(monthChange) {
|
|
1179
|
+
if (this.timePicker) {
|
|
1180
|
+
var hour, minute, second;
|
|
1181
|
+
if (this.#endDate) {
|
|
1182
|
+
hour = parseInt(this.container.querySelector(".start-time .hourselect").value, 10);
|
|
1183
|
+
if (isNaN(hour))
|
|
1184
|
+
hour = parseInt(this.container.querySelector(".start-time .hourselect option:last-child").value, 10);
|
|
1185
|
+
minute = 0;
|
|
1186
|
+
if (this.timePickerOpts.showMinutes) {
|
|
1187
|
+
minute = parseInt(this.container.querySelector(".start-time .minuteselect").value, 10);
|
|
1188
|
+
if (isNaN(minute))
|
|
1189
|
+
minute = parseInt(this.container.querySelector(".start-time .minuteselect option:last-child").value, 10);
|
|
1190
|
+
}
|
|
1191
|
+
second = 0;
|
|
1192
|
+
if (this.timePickerOpts.showSeconds) {
|
|
1193
|
+
second = parseInt(this.container.querySelector(".start-time .secondselect").value, 10);
|
|
1194
|
+
if (isNaN(second))
|
|
1195
|
+
second = parseInt(this.container.querySelector(".start-time .secondselect option:last-child").value, 10);
|
|
1196
|
+
}
|
|
1197
|
+
} else {
|
|
1198
|
+
hour = parseInt(this.container.querySelector(".end-time .hourselect").value, 10);
|
|
1199
|
+
if (isNaN(hour))
|
|
1200
|
+
hour = parseInt(this.container.querySelector(".end-time .hourselect option:last-child").value, 10);
|
|
1201
|
+
minute = 0;
|
|
1202
|
+
if (this.timePickerOpts.showMinutes) {
|
|
1203
|
+
minute = parseInt(this.container.querySelector(".end-time .minuteselect").value, 10);
|
|
1204
|
+
if (isNaN(minute))
|
|
1205
|
+
minute = parseInt(this.container.querySelector(".end-time .minuteselect option:last-child").value, 10);
|
|
1206
|
+
}
|
|
1207
|
+
second = 0;
|
|
1208
|
+
if (this.timePickerOpts.showSeconds) {
|
|
1209
|
+
second = parseInt(this.container.querySelector(".end-time .secondselect").value, 10);
|
|
1210
|
+
if (isNaN(second))
|
|
1211
|
+
second = parseInt(this.container.querySelector(".end-time .secondselect option:last-child").value, 10);
|
|
1212
|
+
}
|
|
1213
|
+
}
|
|
1214
|
+
this.leftCalendar.month = this.leftCalendar.month.set({ hour, minute, second });
|
|
1215
|
+
if (!this.singleMonthView)
|
|
1216
|
+
this.rightCalendar.month = this.rightCalendar.month.set({ hour, minute, second });
|
|
1217
|
+
} else {
|
|
1218
|
+
this.leftCalendar.month = this.leftCalendar.month.set({ hour: 0, minute: 0, second: 0 });
|
|
1219
|
+
if (!this.singleMonthView)
|
|
1220
|
+
this.rightCalendar.month = this.rightCalendar.month.set({ hour: 0, minute: 0, second: 0 });
|
|
1221
|
+
}
|
|
1222
|
+
this.triggerEvent(this.#events.onBeforeRenderCalendar);
|
|
1223
|
+
this.renderCalendar("left");
|
|
1224
|
+
this.renderCalendar("right");
|
|
1225
|
+
if (monthChange)
|
|
1226
|
+
this.triggerEvent(this.#events.onMonthViewChange);
|
|
1227
|
+
this.container.querySelectorAll(".ranges li").forEach((el) => {
|
|
1228
|
+
el.classList.remove("active");
|
|
1229
|
+
});
|
|
1230
|
+
if (this.#endDate == null) return;
|
|
1231
|
+
this.calculateChosenLabel();
|
|
1232
|
+
}
|
|
1233
|
+
/**
|
|
1234
|
+
* Renders the calendar month
|
|
1235
|
+
* @private
|
|
1236
|
+
*/
|
|
1237
|
+
renderCalendar(side) {
|
|
1238
|
+
if (side === "right" && this.singleMonthView)
|
|
1239
|
+
return;
|
|
1240
|
+
var calendar = side === "left" ? this.leftCalendar : this.rightCalendar;
|
|
1241
|
+
if (calendar.month == null && !this.#startDate && this.initalMonth)
|
|
1242
|
+
calendar.month = this.initalMonth.startOf("month");
|
|
1243
|
+
const firstDay = calendar.month.startOf("month");
|
|
1244
|
+
const lastDay = calendar.month.endOf("month").startOf("day");
|
|
1245
|
+
var theDate = calendar.month.startOf("month").minus({ day: 1 });
|
|
1246
|
+
const time = { hour: calendar.month.hour, minute: calendar.month.minute, second: calendar.month.second };
|
|
1247
|
+
var calendar = [];
|
|
1248
|
+
calendar.firstDay = firstDay;
|
|
1249
|
+
calendar.lastDay = lastDay;
|
|
1250
|
+
for (var i = 0; i < 6; i++)
|
|
1251
|
+
calendar[i] = [];
|
|
1252
|
+
while (theDate.weekday != this.locale.firstDay)
|
|
1253
|
+
theDate = theDate.minus({ day: 1 });
|
|
1254
|
+
for (let col = 0, row = -1; col < 42; col++, theDate = theDate.plus({ day: 1 })) {
|
|
1255
|
+
if (col % 7 === 0)
|
|
1256
|
+
row++;
|
|
1257
|
+
calendar[row][col % 7] = theDate.set(time);
|
|
1258
|
+
}
|
|
1259
|
+
if (side === "left") {
|
|
1260
|
+
this.leftCalendar.calendar = calendar;
|
|
1261
|
+
} else {
|
|
1262
|
+
this.rightCalendar.calendar = calendar;
|
|
1263
|
+
}
|
|
1264
|
+
var minDate = side === "left" ? this.minDate : this.#startDate;
|
|
1265
|
+
var maxDate = this.maxDate;
|
|
1266
|
+
var html = "<tr>";
|
|
1267
|
+
if (this.showWeekNumbers || this.showISOWeekNumbers)
|
|
1268
|
+
html += "<th></th>";
|
|
1269
|
+
if ((!minDate || minDate < calendar.firstDay) && (!this.linkedCalendars || side === "left")) {
|
|
1270
|
+
html += '<th class="prev available"><span></span></th>';
|
|
1271
|
+
} else {
|
|
1272
|
+
html += "<th></th>";
|
|
1273
|
+
}
|
|
1274
|
+
var dateHtml = `${this.locale.monthNames[calendar.firstDay.month - 1]} ${calendar.firstDay.year}`;
|
|
1275
|
+
if (this.showDropdowns) {
|
|
1276
|
+
const maxYear = (maxDate && maxDate.year) ?? this.maxYear;
|
|
1277
|
+
const minYear = (minDate && minDate.year) ?? this.minYear;
|
|
1278
|
+
let div = this.externalStyle === "bulma" ? '<div class="select is-small mr-1">' : "";
|
|
1279
|
+
var monthHtml = `${div}<select class="monthselect">`;
|
|
1280
|
+
for (var m = 1; m <= 12; m++) {
|
|
1281
|
+
monthHtml += `<option value="${m}"${m === calendar.firstDay.month ? " selected" : ""}`;
|
|
1282
|
+
if (minDate && calendar.firstDay.set({ month: m }) < minDate.startOf("month") || maxDate && calendar.firstDay.set({ month: m }) > maxDate.endOf("month"))
|
|
1283
|
+
monthHtml += ` disabled`;
|
|
1284
|
+
monthHtml += `>${this.locale.monthNames[m - 1]}</option>`;
|
|
1285
|
+
}
|
|
1286
|
+
monthHtml += "</select>";
|
|
1287
|
+
if (this.externalStyle === "bulma")
|
|
1288
|
+
monthHtml += "</div>";
|
|
1289
|
+
div = this.externalStyle === "bulma" ? '<div class="select is-small ml-1">' : "";
|
|
1290
|
+
var yearHtml = `${div}<select class="yearselect">`;
|
|
1291
|
+
for (var y = minYear; y <= maxYear; y++)
|
|
1292
|
+
yearHtml += `<option value="${y}"${y === calendar.firstDay.year ? " selected" : ""}>${y}</option>`;
|
|
1293
|
+
yearHtml += "</select>";
|
|
1294
|
+
if (this.externalStyle === "bulma")
|
|
1295
|
+
yearHtml += "</div>";
|
|
1296
|
+
dateHtml = monthHtml + yearHtml;
|
|
1297
|
+
}
|
|
1298
|
+
html += '<th colspan="5" class="month">' + dateHtml + "</th>";
|
|
1299
|
+
if ((!maxDate || maxDate > calendar.lastDay.endOf("day")) && (!this.linkedCalendars || side === "right" || this.singleDatePicker || this.singleMonthView)) {
|
|
1300
|
+
html += '<th class="next available"><span></span></th>';
|
|
1301
|
+
} else {
|
|
1302
|
+
html += "<th></th>";
|
|
1303
|
+
}
|
|
1304
|
+
html += "</tr>";
|
|
1305
|
+
html += "<tr>";
|
|
1306
|
+
if (this.showWeekNumbers || this.showISOWeekNumbers)
|
|
1307
|
+
html += `<th class="week">${this.locale.weekLabel}</th>`;
|
|
1308
|
+
for (let [index, dayOfWeek] of this.locale.daysOfWeek.entries()) {
|
|
1309
|
+
html += "<th";
|
|
1310
|
+
if (this.weekendDayClasses && this.weekendDayClasses.length && luxon.Info.getWeekendWeekdays().includes(index + 1))
|
|
1311
|
+
html += ` class="${this.weekendDayClasses}"`;
|
|
1312
|
+
html += `>${dayOfWeek}</th>`;
|
|
1313
|
+
}
|
|
1314
|
+
html += "</tr>";
|
|
1315
|
+
this.container.querySelector(`.drp-calendar.${side} .calendar-table thead`).innerHTML = html;
|
|
1316
|
+
html = "";
|
|
1317
|
+
if (this.#endDate == null && this.maxSpan) {
|
|
1318
|
+
var maxLimit = this.#startDate.plus(this.maxSpan).endOf("day");
|
|
1319
|
+
if (!maxDate || maxLimit < maxDate) {
|
|
1320
|
+
maxDate = maxLimit;
|
|
1321
|
+
}
|
|
1322
|
+
}
|
|
1323
|
+
var minLimit;
|
|
1324
|
+
if (this.#endDate == null && this.minSpan)
|
|
1325
|
+
minLimit = this.#startDate.plus(this.minSpan).startOf("day");
|
|
1326
|
+
for (let row = 0; row < 6; row++) {
|
|
1327
|
+
html += "<tr>";
|
|
1328
|
+
if (this.showISOWeekNumbers)
|
|
1329
|
+
html += `<td class="week">${calendar[row][0].weekNumber}</td>`;
|
|
1330
|
+
else if (this.showWeekNumbers)
|
|
1331
|
+
html += `<td class="week">${calendar[row][0].localWeekNumber}</td>`;
|
|
1332
|
+
for (let col = 0; col < 7; col++) {
|
|
1333
|
+
var classes = [];
|
|
1334
|
+
if (this.todayClasses && this.todayClasses.length && calendar[row][col].hasSame(luxon.DateTime.now(), "day"))
|
|
1335
|
+
classes.push(this.todayClasses);
|
|
1336
|
+
if (this.weekendClasses && this.weekendClasses.length && luxon.Info.getWeekendWeekdays().includes(calendar[row][col].weekday))
|
|
1337
|
+
classes.push(this.weekendClasses);
|
|
1338
|
+
if (calendar[row][col].month != calendar[1][1].month)
|
|
1339
|
+
classes.push("off", "ends");
|
|
1340
|
+
if (this.minDate && calendar[row][col].startOf("day") < this.minDate.startOf("day"))
|
|
1341
|
+
classes.push("off", "disabled");
|
|
1342
|
+
if (maxDate && calendar[row][col].startOf("day") > maxDate.startOf("day"))
|
|
1343
|
+
classes.push("off", "disabled");
|
|
1344
|
+
if (minLimit && calendar[row][col].startOf("day") > this.#startDate.startOf("day") && calendar[row][col].startOf("day") < minLimit.startOf("day"))
|
|
1345
|
+
classes.push("off", "disabled");
|
|
1346
|
+
if (this.isInvalidDate(calendar[row][col]))
|
|
1347
|
+
classes.push("off", "disabled");
|
|
1348
|
+
if (this.#startDate != null && calendar[row][col].hasSame(this.#startDate, "day"))
|
|
1349
|
+
classes.push("active", "start-date");
|
|
1350
|
+
if (this.#endDate != null && calendar[row][col].hasSame(this.#endDate, "day"))
|
|
1351
|
+
classes.push("active", "end-date");
|
|
1352
|
+
if (this.#endDate != null && calendar[row][col] > this.#startDate && calendar[row][col] < this.#endDate)
|
|
1353
|
+
classes.push("in-range");
|
|
1354
|
+
var isCustom = this.isCustomDate(calendar[row][col]);
|
|
1355
|
+
if (isCustom !== false)
|
|
1356
|
+
typeof isCustom === "string" ? classes.push(isCustom) : classes.push(...isCustom);
|
|
1357
|
+
if (!classes.includes("disabled"))
|
|
1358
|
+
classes.push("available");
|
|
1359
|
+
html += `<td class="${classes.join(" ")}" data-title="r${row}c${col}">${calendar[row][col].day}</td>`;
|
|
1360
|
+
}
|
|
1361
|
+
html += "</tr>";
|
|
1362
|
+
}
|
|
1363
|
+
this.container.querySelector(`.drp-calendar.${side} .calendar-table tbody`).innerHTML = html;
|
|
1364
|
+
}
|
|
1365
|
+
/**
|
|
1366
|
+
* Renders the time pickers
|
|
1367
|
+
* @private
|
|
1368
|
+
* @emits "beforeRenderTimePicker"
|
|
1369
|
+
*/
|
|
1370
|
+
renderTimePicker(side) {
|
|
1371
|
+
if (side === "end" && !this.#endDate) return;
|
|
1372
|
+
var selected, minLimit, minDate, maxDate = this.maxDate;
|
|
1373
|
+
let html = "";
|
|
1374
|
+
if (this.showWeekNumbers || this.showISOWeekNumbers)
|
|
1375
|
+
html += "<th></th>";
|
|
1376
|
+
if (this.maxSpan && (!this.maxDate || this.#startDate.plus(this.maxSpan) < this.maxDate))
|
|
1377
|
+
maxDate = this.#startDate.plus(this.maxSpan);
|
|
1378
|
+
if (this.minSpan && side === "end")
|
|
1379
|
+
minLimit = this.#startDate.plus(this.defaultSpan ?? this.minSpan);
|
|
1380
|
+
if (side === "start") {
|
|
1381
|
+
selected = this.#startDate;
|
|
1382
|
+
minDate = this.minDate;
|
|
1383
|
+
} else if (side === "end") {
|
|
1384
|
+
selected = this.#endDate;
|
|
1385
|
+
minDate = this.#startDate;
|
|
1386
|
+
let timeSelector = this.container.querySelector(".drp-calendar .calendar-time.end-time");
|
|
1387
|
+
if (timeSelector.innerHTML != "") {
|
|
1388
|
+
selected = selected.set({
|
|
1389
|
+
hour: !isNaN(selected.hour) ? selected.hour : timeSelector.querySelector(".hourselect option:selected").value,
|
|
1390
|
+
minute: !isNaN(selected.minute) ? selected.minute : timeSelector.querySelector(".minuteselect option:selected").value,
|
|
1391
|
+
second: !isNaN(selected.second) ? selected.second : timeSelector.querySelector(".secondselect option:selected").value
|
|
1392
|
+
});
|
|
1393
|
+
}
|
|
1394
|
+
if (selected < this.#startDate)
|
|
1395
|
+
selected = this.#startDate;
|
|
1396
|
+
if (maxDate && selected > maxDate)
|
|
1397
|
+
selected = maxDate;
|
|
1398
|
+
}
|
|
1399
|
+
html += `<th colspan="7">`;
|
|
1400
|
+
if (this.externalStyle === "bulma")
|
|
1401
|
+
html += '<div class="select is-small mx-1">';
|
|
1402
|
+
html += '<select class="hourselect">';
|
|
1403
|
+
const ampm = selected.toFormat("a", { locale: "en-US" });
|
|
1404
|
+
let start = 0;
|
|
1405
|
+
if (!this.timePicker24Hour)
|
|
1406
|
+
start = ampm === "AM" ? 1 : 13;
|
|
1407
|
+
for (var i = start; i <= start + 23; i += this.timePickerOpts.hourStep) {
|
|
1408
|
+
let time = selected.set({ hour: i % 24 });
|
|
1409
|
+
let disabled = false;
|
|
1410
|
+
if (minDate && time.set({ minute: 59 }) < minDate)
|
|
1411
|
+
disabled = true;
|
|
1412
|
+
if (maxDate && time.set({ minute: 0 }) > maxDate)
|
|
1413
|
+
disabled = true;
|
|
1414
|
+
if (minLimit && time.endOf("hour") < minLimit)
|
|
1415
|
+
disabled = true;
|
|
1416
|
+
if (!disabled && this.isInvalidTime(time, this.singleDatePicker ? null : side, "hour"))
|
|
1417
|
+
disabled = true;
|
|
1418
|
+
if (this.timePicker24Hour) {
|
|
1419
|
+
if (!disabled && i == selected.hour) {
|
|
1420
|
+
html += `<option value="${i}" selected>${i}</option>`;
|
|
1421
|
+
} else if (disabled) {
|
|
1422
|
+
html += `<option value="${i}" disabled class="disabled">${i}</option>`;
|
|
1423
|
+
} else {
|
|
1424
|
+
html += `<option value="${i}">${i}</option>`;
|
|
1425
|
+
}
|
|
1426
|
+
} else {
|
|
1427
|
+
const i_12 = luxon.DateTime.fromFormat(`${i % 24}`, "H").toFormat("h");
|
|
1428
|
+
const i_ampm = luxon.DateTime.fromFormat(`${i % 24}`, "H").toFormat("a", { locale: "en-US" });
|
|
1429
|
+
if (ampm == i_ampm) {
|
|
1430
|
+
if (!disabled && i == selected.hour) {
|
|
1431
|
+
html += `<option ampm="${i_ampm}" value="${i % 24}" selected>${i_12}</option>`;
|
|
1432
|
+
} else if (disabled) {
|
|
1433
|
+
html += `<option ampm="${i_ampm}" value="${i % 24}" disabled class="disabled">${i_12}</option>`;
|
|
1434
|
+
} else {
|
|
1435
|
+
html += `<option ampm="${i_ampm}" value="${i % 24}">${i_12}</option>`;
|
|
1436
|
+
}
|
|
1437
|
+
} else {
|
|
1438
|
+
html += `<option ampm="${i_ampm}" hidden="hidden" value="${i % 24}">${i_12}</option>`;
|
|
1439
|
+
}
|
|
1440
|
+
}
|
|
1441
|
+
}
|
|
1442
|
+
html += "</select>";
|
|
1443
|
+
if (this.externalStyle === "bulma")
|
|
1444
|
+
html += "</div>";
|
|
1445
|
+
if (this.timePickerOpts.showMinutes) {
|
|
1446
|
+
html += " : ";
|
|
1447
|
+
if (this.externalStyle === "bulma")
|
|
1448
|
+
html += '<div class="select is-small mx-1">';
|
|
1449
|
+
html += '<select class="minuteselect">';
|
|
1450
|
+
for (var i = 0; i < 60; i += this.timePickerOpts.minuteStep) {
|
|
1451
|
+
var padded = i < 10 ? "0" + i : i;
|
|
1452
|
+
let time = selected.set({ minute: i });
|
|
1453
|
+
let disabled = false;
|
|
1454
|
+
if (minDate && time.set({ second: 59 }) < minDate)
|
|
1455
|
+
disabled = true;
|
|
1456
|
+
if (maxDate && time.set({ second: 0 }) > maxDate)
|
|
1457
|
+
disabled = true;
|
|
1458
|
+
if (minLimit && time.endOf("minute") < minLimit)
|
|
1459
|
+
disabled = true;
|
|
1460
|
+
if (!disabled && this.isInvalidTime(time, this.singleDatePicker ? null : side, "minute"))
|
|
1461
|
+
disabled = true;
|
|
1462
|
+
if (selected.minute == i && !disabled) {
|
|
1463
|
+
html += `<option value="${i}" selected>${padded}</option>`;
|
|
1464
|
+
} else if (disabled) {
|
|
1465
|
+
html += `<option value="${i}" disabled class="disabled">${padded}</option>`;
|
|
1466
|
+
} else {
|
|
1467
|
+
html += `<option value="${i}">${padded}</option>`;
|
|
1468
|
+
}
|
|
1469
|
+
}
|
|
1470
|
+
html += "</select>";
|
|
1471
|
+
if (this.externalStyle === "bulma")
|
|
1472
|
+
html += "</div>";
|
|
1473
|
+
}
|
|
1474
|
+
if (this.timePickerOpts.showSeconds) {
|
|
1475
|
+
html += " : ";
|
|
1476
|
+
if (this.externalStyle === "bulma")
|
|
1477
|
+
html += '<div class="select is-small mx-1">';
|
|
1478
|
+
html += '<select class="secondselect">';
|
|
1479
|
+
for (var i = 0; i < 60; i += this.timePickerOpts.secondStep) {
|
|
1480
|
+
var padded = i < 10 ? "0" + i : i;
|
|
1481
|
+
let time = selected.set({ second: i });
|
|
1482
|
+
let disabled = false;
|
|
1483
|
+
if (minDate && time < minDate)
|
|
1484
|
+
disabled = true;
|
|
1485
|
+
if (maxDate && time > maxDate)
|
|
1486
|
+
disabled = true;
|
|
1487
|
+
if (minLimit && time < minLimit)
|
|
1488
|
+
disabled = true;
|
|
1489
|
+
if (!disabled && this.isInvalidTime(time, this.singleDatePicker ? null : side, "second"))
|
|
1490
|
+
disabled = true;
|
|
1491
|
+
if (selected.second == i && !disabled) {
|
|
1492
|
+
html += `<option value="${i}" selected>${padded}</option>`;
|
|
1493
|
+
} else if (disabled) {
|
|
1494
|
+
html += `<option value="${i}" disabled class="disabled">${padded}</option>`;
|
|
1495
|
+
} else {
|
|
1496
|
+
html += `<option value="${i}">${padded}</option>`;
|
|
1497
|
+
}
|
|
1498
|
+
}
|
|
1499
|
+
html += "</select>";
|
|
1500
|
+
if (this.externalStyle === "bulma")
|
|
1501
|
+
html += "</div>";
|
|
1502
|
+
}
|
|
1503
|
+
if (!this.timePicker24Hour) {
|
|
1504
|
+
if (this.externalStyle === "bulma")
|
|
1505
|
+
html += '<div class="select is-small mx-1">';
|
|
1506
|
+
html += '<select class="ampmselect">';
|
|
1507
|
+
var am_html = "";
|
|
1508
|
+
var pm_html = "";
|
|
1509
|
+
let disabled = false;
|
|
1510
|
+
if (minDate && selected.startOf("day") < minDate)
|
|
1511
|
+
disabled = true;
|
|
1512
|
+
if (maxDate && selected.endOf("day") > maxDate)
|
|
1513
|
+
disabled = true;
|
|
1514
|
+
if (minLimit && selected.startOf("day") < minLimit)
|
|
1515
|
+
disabled = true;
|
|
1516
|
+
if (disabled) {
|
|
1517
|
+
am_html = ' disabled class="disabled "';
|
|
1518
|
+
pm_html = ' disabled class="disabled"';
|
|
1519
|
+
} else {
|
|
1520
|
+
if (this.isInvalidTime(selected, this.singleDatePicker ? null : side, "ampm")) {
|
|
1521
|
+
if (selected.toFormat("a", { locale: "en-US" }) === "AM") {
|
|
1522
|
+
pm_html = ' disabled class="disabled"';
|
|
1523
|
+
} else {
|
|
1524
|
+
am_html = ' disabled class="disabled"';
|
|
1525
|
+
}
|
|
1526
|
+
}
|
|
1527
|
+
}
|
|
1528
|
+
html += `<option value="AM"${am_html}`;
|
|
1529
|
+
if (selected.toFormat("a", { locale: "en-US" }) === "AM")
|
|
1530
|
+
html += " selected";
|
|
1531
|
+
html += `>${luxon.Info.meridiems()[0]}</option><option value="PM"${pm_html}`;
|
|
1532
|
+
if (selected.toFormat("a", { locale: "en-US" }) === "PM")
|
|
1533
|
+
html += " selected";
|
|
1534
|
+
html += `>${luxon.Info.meridiems()[1]}</option>`;
|
|
1535
|
+
html += "</select>";
|
|
1536
|
+
if (this.externalStyle === "bulma")
|
|
1537
|
+
html += "</div>";
|
|
1538
|
+
}
|
|
1539
|
+
html += "</div></th>";
|
|
1540
|
+
this.container.querySelector(`.drp-calendar .calendar-time.${side}-time`).innerHTML = html;
|
|
1541
|
+
}
|
|
1542
|
+
/**
|
|
1543
|
+
* Disable the `Apply` button if no date value is selected
|
|
1544
|
+
* @private
|
|
1545
|
+
*/
|
|
1546
|
+
setApplyBtnState() {
|
|
1547
|
+
const state = this.singleDatePicker || this.#endDate && this.#startDate <= this.#endDate;
|
|
1548
|
+
this.container.querySelector("button.applyBtn").disabled = !state;
|
|
1549
|
+
}
|
|
1550
|
+
/* #endregion */
|
|
1551
|
+
/* #region Move/Show/Hide */
|
|
1552
|
+
/**
|
|
1553
|
+
* Place the picker at the right place in the document
|
|
1554
|
+
*/
|
|
1555
|
+
move() {
|
|
1556
|
+
let parentOffset = { top: 0, left: 0 };
|
|
1557
|
+
let containerTop;
|
|
1558
|
+
let containerLeft;
|
|
1559
|
+
let drops = this.drops;
|
|
1560
|
+
let parentRightEdge = window.innerWidth;
|
|
1561
|
+
if (!this.parentEl.matches("body")) {
|
|
1562
|
+
parentOffset = {
|
|
1563
|
+
top: offset(this.parentEl).top - this.parentEl.scrollTop(),
|
|
1564
|
+
left: offset(this.parentEl).left - this.parentEl.scrollLeft()
|
|
1565
|
+
};
|
|
1566
|
+
parentRightEdge = this.parentEl[0].clientWidth + offset(this.parentEl).left;
|
|
1567
|
+
}
|
|
1568
|
+
switch (this.drops) {
|
|
1569
|
+
case "auto":
|
|
1570
|
+
containerTop = offset(this.element).top + outerHeight(this.element) - parentOffset.top;
|
|
1571
|
+
if (containerTop + outerHeight(this.container) >= this.parentEl.scrollHeight) {
|
|
1572
|
+
containerTop = offset(this.element).top - outerHeight(this.container) - parentOffset.top;
|
|
1573
|
+
drops = "up";
|
|
1574
|
+
}
|
|
1575
|
+
break;
|
|
1576
|
+
case "up":
|
|
1577
|
+
containerTop = offset(this.element).top - outerHeight(this.container) - parentOffset.top;
|
|
1578
|
+
break;
|
|
1579
|
+
case "down":
|
|
1580
|
+
containerTop = offset(this.element).top + outerHeight(this.element) - parentOffset.top;
|
|
1581
|
+
break;
|
|
1582
|
+
default:
|
|
1583
|
+
console.error(`Option drops '${drops}' not defined`);
|
|
1584
|
+
break;
|
|
1585
|
+
}
|
|
1586
|
+
for (const [key2, value] of Object.entries({ top: 0, left: 0, right: "auto" }))
|
|
1587
|
+
this.container.style[key2] = typeof value === "number" && value > 0 ? `${value}px` : value;
|
|
1588
|
+
const containerWidth = outerWidth(this.container);
|
|
1589
|
+
this.container.classList.toggle("drop-up", drops === "up");
|
|
1590
|
+
switch (this.opens) {
|
|
1591
|
+
case "left":
|
|
1592
|
+
const containerRight = parentRightEdge - offset(this.element).left - outerWidth(this.element);
|
|
1593
|
+
if (containerWidth + containerRight > window.innerWidth) {
|
|
1594
|
+
for (const [key2, value] of Object.entries({ top: containerTop, right: "auto", left: 9 }))
|
|
1595
|
+
this.container.style[key2] = typeof value === "number" && value > 0 ? `${value}px` : value;
|
|
1596
|
+
} else {
|
|
1597
|
+
for (const [key2, value] of Object.entries({ top: containerTop, right: containerRight, left: "auto" }))
|
|
1598
|
+
this.container.style[key2] = typeof value === "number" && value > 0 ? `${value}px` : value;
|
|
1599
|
+
}
|
|
1600
|
+
break;
|
|
1601
|
+
case "center":
|
|
1602
|
+
containerLeft = offset(this.element).left - parentOffset.left + outerWidth(this.element) / 2 - containerWidth / 2;
|
|
1603
|
+
if (containerLeft < 0) {
|
|
1604
|
+
for (const [key2, value] of Object.entries({ top: containerTop, right: "auto", left: 9 }))
|
|
1605
|
+
this.container.style[key2] = typeof value === "number" && value > 0 ? `${value}px` : value;
|
|
1606
|
+
} else if (containerLeft + containerWidth > window.innerWidth) {
|
|
1607
|
+
for (const [key2, value] of Object.entries({ top: containerTop, left: "auto", right: 0 }))
|
|
1608
|
+
this.container.style[key2] = typeof value === "number" && value > 0 ? `${value}px` : value;
|
|
1609
|
+
} else {
|
|
1610
|
+
for (const [key2, value] of Object.entries({ top: containerTop, left: containerLeft, right: "auto" }))
|
|
1611
|
+
this.container.style[key2] = typeof value === "number" && value > 0 ? `${value}px` : value;
|
|
1612
|
+
}
|
|
1613
|
+
break;
|
|
1614
|
+
case "right":
|
|
1615
|
+
containerLeft = offset(this.element).left - parentOffset.left;
|
|
1616
|
+
if (containerLeft + containerWidth > window.innerWidth) {
|
|
1617
|
+
for (const [key2, value] of Object.entries({ top: containerTop, left: "auto", right: 0 }))
|
|
1618
|
+
this.container.style[key2] = typeof value === "number" && value > 0 ? `${value}px` : value;
|
|
1619
|
+
} else {
|
|
1620
|
+
for (const [key2, value] of Object.entries({ top: `${containerTop}px`, left: containerLeft, right: "auto" }))
|
|
1621
|
+
this.container.style[key2] = typeof value === "number" && value > 0 ? `${value}px` : value;
|
|
1622
|
+
}
|
|
1623
|
+
break;
|
|
1624
|
+
default:
|
|
1625
|
+
console.error(`Option opens '${this.opens}' not defined`);
|
|
1626
|
+
break;
|
|
1627
|
+
}
|
|
1628
|
+
}
|
|
1629
|
+
/**
|
|
1630
|
+
* Shows the picker
|
|
1631
|
+
* @emits "show"
|
|
1632
|
+
*/
|
|
1633
|
+
show() {
|
|
1634
|
+
if (this.isShowing) return;
|
|
1635
|
+
document.addEventListener("mousedown", this.#outsideClickProxy);
|
|
1636
|
+
document.addEventListener("touchend", this.#outsideClickProxy);
|
|
1637
|
+
document.addEventListener("click", this.#dropdownClickWrapper);
|
|
1638
|
+
document.addEventListener("focusin", this.#outsideClickProxy);
|
|
1639
|
+
window.addEventListener("resize", this.#onResizeProxy);
|
|
1640
|
+
this.oldStartDate = this.#startDate;
|
|
1641
|
+
this.oldEndDate = this.#endDate;
|
|
1642
|
+
this.updateView(false);
|
|
1643
|
+
this.container.style.display = "block";
|
|
1644
|
+
this.move();
|
|
1645
|
+
this.triggerEvent(this.#events.onShow);
|
|
1646
|
+
this.isShowing = true;
|
|
1647
|
+
}
|
|
1648
|
+
/**
|
|
1649
|
+
* Hides the picker
|
|
1650
|
+
* @emits "beforeHide"
|
|
1651
|
+
* @emits "hide"
|
|
1652
|
+
*/
|
|
1653
|
+
hide() {
|
|
1654
|
+
if (!this.isShowing) return;
|
|
1655
|
+
if (!this.#endDate) {
|
|
1656
|
+
this.#startDate = this.oldStartDate;
|
|
1657
|
+
this.#endDate = this.oldEndDate;
|
|
1658
|
+
}
|
|
1659
|
+
if (!this.#startDate.equals(this.oldStartDate) || !this.#endDate.equals(this.oldEndDate))
|
|
1660
|
+
this.callback(this.startDate, this.endDate, this.chosenLabel);
|
|
1661
|
+
this.updateElement();
|
|
1662
|
+
const event = this.triggerEvent(this.#events.onBeforeHide);
|
|
1663
|
+
if (event.defaultPrevented)
|
|
1664
|
+
return;
|
|
1665
|
+
document.removeEventListener("mousedown", this.#outsideClickProxy);
|
|
1666
|
+
document.removeEventListener("touchend", this.#outsideClickProxy);
|
|
1667
|
+
document.removeEventListener("focusin", this.#outsideClickProxy);
|
|
1668
|
+
document.removeEventListener("click", this.#dropdownClickWrapper);
|
|
1669
|
+
window.removeEventListener("resize", this.#onResizeProxy);
|
|
1670
|
+
this.container.style.display = "none";
|
|
1671
|
+
this.triggerEvent(this.#events.onHide);
|
|
1672
|
+
this.isShowing = false;
|
|
1673
|
+
}
|
|
1674
|
+
/**
|
|
1675
|
+
* Toggles visibility of the picker
|
|
1676
|
+
*/
|
|
1677
|
+
toggle() {
|
|
1678
|
+
if (this.isShowing) {
|
|
1679
|
+
this.hide();
|
|
1680
|
+
} else {
|
|
1681
|
+
this.show();
|
|
1682
|
+
}
|
|
1683
|
+
}
|
|
1684
|
+
/**
|
|
1685
|
+
* Shows calendar when user selects "Custom Ranges"
|
|
1686
|
+
* @emits "showCalendar"
|
|
1687
|
+
*/
|
|
1688
|
+
showCalendars() {
|
|
1689
|
+
this.container.classList.add("show-calendar");
|
|
1690
|
+
this.move();
|
|
1691
|
+
this.triggerEvent(this.#events.onShowCalendar);
|
|
1692
|
+
}
|
|
1693
|
+
/**
|
|
1694
|
+
* Hides calendar when user selects a predefined range
|
|
1695
|
+
* @emits "hideCalendar"
|
|
1696
|
+
*/
|
|
1697
|
+
hideCalendars() {
|
|
1698
|
+
this.container.classList.remove("show-calendar");
|
|
1699
|
+
this.triggerEvent(this.#events.onHideCalendar);
|
|
1700
|
+
}
|
|
1701
|
+
/* #endregion */
|
|
1702
|
+
/* #region Handle mouse related events */
|
|
1703
|
+
/**
|
|
1704
|
+
* Closes the picker when user clicks outside
|
|
1705
|
+
* @param {external:Event} e - The Event target
|
|
1706
|
+
* @emits "outsideClick"
|
|
1707
|
+
* @private
|
|
1708
|
+
*/
|
|
1709
|
+
outsideClick(e) {
|
|
1710
|
+
const target = e.target;
|
|
1711
|
+
function closest2(el, selector) {
|
|
1712
|
+
let parent = el.parentElement;
|
|
1713
|
+
while (parent) {
|
|
1714
|
+
if (parent == selector)
|
|
1715
|
+
return parent;
|
|
1716
|
+
parent = parent.parentElement;
|
|
1717
|
+
}
|
|
1718
|
+
return null;
|
|
1719
|
+
}
|
|
1720
|
+
if (
|
|
1721
|
+
// ie modal dialog fix
|
|
1722
|
+
e.type === "focusin" || closest2(target, this.element) || closest2(target, this.container) || target.closest(".calendar-table")
|
|
1723
|
+
) return;
|
|
1724
|
+
const event = this.triggerEvent(this.#events.onOutsideClick);
|
|
1725
|
+
if (event.defaultPrevented)
|
|
1726
|
+
return;
|
|
1727
|
+
if (this.onOutsideClick === "cancel") {
|
|
1728
|
+
this.#startDate = this.oldStartDate;
|
|
1729
|
+
this.#endDate = this.oldEndDate;
|
|
1730
|
+
}
|
|
1731
|
+
this.hide();
|
|
1732
|
+
}
|
|
1733
|
+
/**
|
|
1734
|
+
* Move calendar to previous month
|
|
1735
|
+
* @param {external:Event} e - The Event target
|
|
1736
|
+
* @private
|
|
1737
|
+
*/
|
|
1738
|
+
clickPrev(e) {
|
|
1739
|
+
let cal = e.target.closest(".drp-calendar");
|
|
1740
|
+
if (cal.classList.contains("left")) {
|
|
1741
|
+
this.leftCalendar.month = this.leftCalendar.month.minus({ month: 1 });
|
|
1742
|
+
if (this.linkedCalendars && !this.singleMonthView)
|
|
1743
|
+
this.rightCalendar.month = this.rightCalendar.month.minus({ month: 1 });
|
|
1744
|
+
} else {
|
|
1745
|
+
this.rightCalendar.month = this.rightCalendar.month.minus({ month: 1 });
|
|
1746
|
+
}
|
|
1747
|
+
this.updateCalendars(true);
|
|
1748
|
+
}
|
|
1749
|
+
/**
|
|
1750
|
+
* Move calendar to next month
|
|
1751
|
+
* @param {external:Event} e - The Event target
|
|
1752
|
+
* @private
|
|
1753
|
+
*/
|
|
1754
|
+
clickNext(e) {
|
|
1755
|
+
let cal = e.target.closest(".drp-calendar");
|
|
1756
|
+
if (cal.classList.contains("left")) {
|
|
1757
|
+
this.leftCalendar.month = this.leftCalendar.month.plus({ month: 1 });
|
|
1758
|
+
} else {
|
|
1759
|
+
this.rightCalendar.month = this.rightCalendar.month.plus({ month: 1 });
|
|
1760
|
+
if (this.linkedCalendars)
|
|
1761
|
+
this.leftCalendar.month = this.leftCalendar.month.plus({ month: 1 });
|
|
1762
|
+
}
|
|
1763
|
+
this.updateCalendars(true);
|
|
1764
|
+
}
|
|
1765
|
+
/**
|
|
1766
|
+
* User hovers over date values
|
|
1767
|
+
* @param {external:Event} e - The Event target
|
|
1768
|
+
* @private
|
|
1769
|
+
*/
|
|
1770
|
+
hoverDate(e) {
|
|
1771
|
+
if (!e.target.classList.contains("available")) return;
|
|
1772
|
+
let title = e.target.dataset.title;
|
|
1773
|
+
const row = title.substring(1, 2);
|
|
1774
|
+
const col = title.substring(3, 4);
|
|
1775
|
+
const cal = e.target(closest, ".drp-calendar");
|
|
1776
|
+
var date = cal.classList.contains("left") ? this.leftCalendar.calendar[row][col] : this.rightCalendar.calendar[row][col];
|
|
1777
|
+
const leftCalendar = this.leftCalendar;
|
|
1778
|
+
const rightCalendar = this.rightCalendar;
|
|
1779
|
+
const startDate = this.#startDate;
|
|
1780
|
+
const initalMonth = this.initalMonth;
|
|
1781
|
+
if (!this.#endDate) {
|
|
1782
|
+
this.container.querySelectorAll(".drp-calendar tbody td").forEach((el) => {
|
|
1783
|
+
if (el.classList.contains("week")) return;
|
|
1784
|
+
const title2 = el.dataset.title;
|
|
1785
|
+
const row2 = title2.substring(1, 2);
|
|
1786
|
+
const col2 = title2.substring(3, 4);
|
|
1787
|
+
const cal2 = el.closest(".drp-calendar");
|
|
1788
|
+
const dt = cal2.classList.contains("left") ? leftCalendar.calendar[row2][col2] : rightCalendar.calendar[row2][col2];
|
|
1789
|
+
if (!startDate && initalMonth) {
|
|
1790
|
+
el.classList.remove("in-range");
|
|
1791
|
+
} else {
|
|
1792
|
+
el.classList.toggle("in-range", dt > startDate && dt < date || dt.hasSame(date, "day"));
|
|
1793
|
+
}
|
|
1794
|
+
});
|
|
1795
|
+
}
|
|
1796
|
+
}
|
|
1797
|
+
/**
|
|
1798
|
+
* User hovers over ranges
|
|
1799
|
+
* @param {external:Event} e - The Event target
|
|
1800
|
+
* @private
|
|
1801
|
+
*/
|
|
1802
|
+
hoverRange(e) {
|
|
1803
|
+
const label = e.target.dataset.rangeKey;
|
|
1804
|
+
const previousDates = [this.#startDate, this.#endDate];
|
|
1805
|
+
const dates = this.ranges[label] ?? [this.#startDate, this.#endDate];
|
|
1806
|
+
const leftCalendar = this.leftCalendar;
|
|
1807
|
+
const rightCalendar = this.rightCalendar;
|
|
1808
|
+
this.container.querySelectorAll(".drp-calendar tbody td").forEach((el) => {
|
|
1809
|
+
if (el.classList.contains("week")) return;
|
|
1810
|
+
const title = el.dataset.ttitle;
|
|
1811
|
+
const row = title.substring(1, 2);
|
|
1812
|
+
const col = title.substring(3, 4);
|
|
1813
|
+
const cal = el.closest(".drp-calendar");
|
|
1814
|
+
const dt = cal.classList.contains("left") ? leftCalendar.calendar[row][col] : rightCalendar.calendar[row][col];
|
|
1815
|
+
el.classList.toggle("start-hover", dt.hasSame(dates[0], "day"));
|
|
1816
|
+
el.classList.toggle("start-date", dt.hasSame(previousDates[0], "day"));
|
|
1817
|
+
el.classList.toggle("end-hover", dt.hasSame(dates[1], "day"));
|
|
1818
|
+
el.classList.toggle("end-date", previousDates[1] != null && dt.hasSame(previousDates[1], "day"));
|
|
1819
|
+
el.classList.toggle("range-hover", dt.startOf("day") >= dates[0].startOf("day") && dt.startOf("day") <= dates[1].startOf("day"));
|
|
1820
|
+
el.classList.toggle("in-range", dt.startOf("day") >= previousDates[0].startOf("day") && previousDates[1] != null && dt.startOf("day") <= previousDates[1].startOf("day"));
|
|
1821
|
+
});
|
|
1822
|
+
}
|
|
1823
|
+
/**
|
|
1824
|
+
* User leave ranges, remove hightlight from dates
|
|
1825
|
+
* @private
|
|
1826
|
+
*/
|
|
1827
|
+
leaveRange() {
|
|
1828
|
+
this.container.querySelectorAll(".drp-calendar tbody td").forEach((el) => {
|
|
1829
|
+
if (el.classList.contains("week")) return;
|
|
1830
|
+
el.classList.remove("start-hover");
|
|
1831
|
+
el.classList.remove("end-hover");
|
|
1832
|
+
el.classList.remove("range-hover");
|
|
1833
|
+
});
|
|
1834
|
+
}
|
|
1835
|
+
/* #endregion */
|
|
1836
|
+
/* #region Select values by Mouse */
|
|
1837
|
+
/**
|
|
1838
|
+
* Set date values after user selected a date
|
|
1839
|
+
* @param {external:Event} e - The Event target
|
|
1840
|
+
* @private
|
|
1841
|
+
*/
|
|
1842
|
+
clickRange(e) {
|
|
1843
|
+
let label = e.target.getAttribute("data-range-key");
|
|
1844
|
+
this.chosenLabel = label;
|
|
1845
|
+
if (label == this.locale.customRangeLabel) {
|
|
1846
|
+
this.showCalendars();
|
|
1847
|
+
} else {
|
|
1848
|
+
let newDate = this.ranges[label];
|
|
1849
|
+
const monthChange = !this.#startDate.hasSame(newDate[0], "month") || !this.#endDate.hasSame(newDate[1], "month");
|
|
1850
|
+
this.#startDate = newDate[0];
|
|
1851
|
+
this.#endDate = newDate[1];
|
|
1852
|
+
if (!this.timePicker) {
|
|
1853
|
+
this.#startDate.startOf("day");
|
|
1854
|
+
this.#endDate.endOf("day");
|
|
1855
|
+
}
|
|
1856
|
+
if (!this.alwaysShowCalendars)
|
|
1857
|
+
this.hideCalendars();
|
|
1858
|
+
const event = this.triggerEvent(this.#events.onBeforeHide);
|
|
1859
|
+
if (event.defaultPrevented)
|
|
1860
|
+
this.updateView(monthChange);
|
|
1861
|
+
this.clickApply();
|
|
1862
|
+
}
|
|
1863
|
+
}
|
|
1864
|
+
/**
|
|
1865
|
+
* User clicked a date
|
|
1866
|
+
* @param {external:Event} e - The Event target
|
|
1867
|
+
* @emits "dateChange"
|
|
1868
|
+
* @private
|
|
1869
|
+
*/
|
|
1870
|
+
clickDate(e) {
|
|
1871
|
+
if (!e.target.classList.contains("available")) return;
|
|
1872
|
+
let title = e.target.dataset.title;
|
|
1873
|
+
let row = title.substring(1, 2);
|
|
1874
|
+
let col = title.substring(3, 4);
|
|
1875
|
+
let cal = e.target.closest(".drp-calendar");
|
|
1876
|
+
let date = cal.classList.contains("left") ? this.leftCalendar.calendar[row][col] : this.rightCalendar.calendar[row][col];
|
|
1877
|
+
let side;
|
|
1878
|
+
if (this.#endDate || !this.#startDate || date < this.#startDate.startOf("day")) {
|
|
1879
|
+
if (this.timePicker) {
|
|
1880
|
+
let hour = parseInt(this.container.querySelector(".start-time .hourselect").value, 10);
|
|
1881
|
+
if (isNaN(hour))
|
|
1882
|
+
hour = parseInt(this.container.querySelector(".start-time .hourselect option:last-child").value, 10);
|
|
1883
|
+
let minute = 0;
|
|
1884
|
+
if (this.timePickerOpts.showMinutes) {
|
|
1885
|
+
minute = parseInt(this.container.querySelector(".start-time .minuteselect").value, 10);
|
|
1886
|
+
if (isNaN(minute))
|
|
1887
|
+
minute = parseInt(this.container.querySelector(".start-time .minuteselect option:last-child").value, 10);
|
|
1888
|
+
}
|
|
1889
|
+
let second = 0;
|
|
1890
|
+
if (this.timePickerOpts.showSeconds) {
|
|
1891
|
+
second = parseInt(this.container.querySelector(".start-time .secondselect").value, 10);
|
|
1892
|
+
if (isNaN(second))
|
|
1893
|
+
second = parseInt(this.container.querySelector(".start-time .secondselect option:last-child").value, 10);
|
|
1894
|
+
}
|
|
1895
|
+
date = date.set({ hour, minute, second });
|
|
1896
|
+
} else {
|
|
1897
|
+
date = date.startOf("day");
|
|
1898
|
+
}
|
|
1899
|
+
this.#endDate = null;
|
|
1900
|
+
this.#startDate = date;
|
|
1901
|
+
side = "start";
|
|
1902
|
+
} else if (!this.#endDate && date < this.#startDate) {
|
|
1903
|
+
this.#endDate = this.#startDate;
|
|
1904
|
+
side = "end";
|
|
1905
|
+
} else {
|
|
1906
|
+
if (this.timePicker) {
|
|
1907
|
+
let hour = parseInt(this.container.querySelector(".end-time .hourselect").value, 10);
|
|
1908
|
+
if (isNaN(hour))
|
|
1909
|
+
hour = parseInt(this.container.querySelector(".end-time .hourselect option:last-child").value, 10);
|
|
1910
|
+
let minute = 0;
|
|
1911
|
+
if (this.timePickerOpts.showMinutes) {
|
|
1912
|
+
minute = parseInt(this.container.querySelector(".end-time .minuteselect").value, 10);
|
|
1913
|
+
if (isNaN(minute))
|
|
1914
|
+
minute = parseInt(this.container.querySelector(".end-time .minuteselect option:last-child").value, 10);
|
|
1915
|
+
}
|
|
1916
|
+
let second = 0;
|
|
1917
|
+
if (this.timePickerOpts.showSeconds) {
|
|
1918
|
+
second = parseInt(this.container.querySelector(".end-time .secondselect").value, 10);
|
|
1919
|
+
if (isNaN(second))
|
|
1920
|
+
second = parseInt(this.container.querySelector(".end-time .secondselect option:last-child").value, 10);
|
|
1921
|
+
}
|
|
1922
|
+
date = date.set({ hour, minute, second });
|
|
1923
|
+
} else {
|
|
1924
|
+
date = date.endOf("day");
|
|
1925
|
+
}
|
|
1926
|
+
this.#endDate = date;
|
|
1927
|
+
if (this.autoApply) {
|
|
1928
|
+
this.calculateChosenLabel();
|
|
1929
|
+
this.clickApply();
|
|
1930
|
+
}
|
|
1931
|
+
side = "end";
|
|
1932
|
+
}
|
|
1933
|
+
if (this.singleDatePicker) {
|
|
1934
|
+
this.#endDate = this.#startDate;
|
|
1935
|
+
if (!this.timePicker && this.autoApply)
|
|
1936
|
+
this.clickApply();
|
|
1937
|
+
side = null;
|
|
1938
|
+
}
|
|
1939
|
+
this.updateView(false);
|
|
1940
|
+
e.stopPropagation();
|
|
1941
|
+
if (this.autoUpdateInput)
|
|
1942
|
+
this.updateElement();
|
|
1943
|
+
this.triggerEvent(this.#events.onDateChange, { side });
|
|
1944
|
+
}
|
|
1945
|
+
/**
|
|
1946
|
+
* Hightlight selected predefined range in calendar
|
|
1947
|
+
* @private
|
|
1948
|
+
*/
|
|
1949
|
+
calculateChosenLabel() {
|
|
1950
|
+
if (Object.keys(this.ranges).length === 0)
|
|
1951
|
+
return;
|
|
1952
|
+
let customRange = true;
|
|
1953
|
+
let unit = this.timePicker ? "hour" : "day";
|
|
1954
|
+
if (this.timePicker) {
|
|
1955
|
+
if (this.timePickerOpts.showMinutes) {
|
|
1956
|
+
unit = "minute";
|
|
1957
|
+
} else if (this.timePickerOpts.showSeconds) {
|
|
1958
|
+
unit = "second";
|
|
1959
|
+
}
|
|
1960
|
+
}
|
|
1961
|
+
for (const [key2, [start, end]] of Object.entries(this.ranges)) {
|
|
1962
|
+
if (this.#startDate.startOf(unit).equals(start.startOf(unit)) && this.#endDate.startOf(unit).equals(end.startOf(unit))) {
|
|
1963
|
+
customRange = false;
|
|
1964
|
+
const range = this.container.querySelector(`.ranges li[data-range-key="${key2}"]`);
|
|
1965
|
+
this.chosenLabel = key2;
|
|
1966
|
+
range.classList.add("active");
|
|
1967
|
+
break;
|
|
1968
|
+
}
|
|
1969
|
+
}
|
|
1970
|
+
if (customRange) {
|
|
1971
|
+
if (this.showCustomRangeLabel) {
|
|
1972
|
+
const range = this.container.querySelector(".ranges li:last-child");
|
|
1973
|
+
this.chosenLabel = range.dataset.rangeKey;
|
|
1974
|
+
range.classList.add("active");
|
|
1975
|
+
} else {
|
|
1976
|
+
this.chosenLabel = null;
|
|
1977
|
+
}
|
|
1978
|
+
this.showCalendars();
|
|
1979
|
+
}
|
|
1980
|
+
}
|
|
1981
|
+
/**
|
|
1982
|
+
* User clicked a time
|
|
1983
|
+
* @param {external:Event} e - The Event target
|
|
1984
|
+
* @emits "timeChange"
|
|
1985
|
+
* @private
|
|
1986
|
+
*/
|
|
1987
|
+
timeChanged(e) {
|
|
1988
|
+
const time = e.target.closest(".calendar-time");
|
|
1989
|
+
const side = time.classList.contains("start-time") ? "start" : "end";
|
|
1990
|
+
var hour = parseInt(time.querySelector(".hourselect").value, 10);
|
|
1991
|
+
if (isNaN(hour))
|
|
1992
|
+
hour = parseInt(time.querySelector(".hourselect option:last-child").value, 10);
|
|
1993
|
+
if (!this.timePicker24Hour) {
|
|
1994
|
+
const ampm = time.querySelector(".ampmselect").value;
|
|
1995
|
+
if (ampm == null)
|
|
1996
|
+
time.querySelector(".ampmselect option:last-child").value;
|
|
1997
|
+
if (ampm != luxon.DateTime.fromFormat(`${hour}`, "H").toFormat("a", { locale: "en-US" })) {
|
|
1998
|
+
time.querySelectorAll(".hourselect > option").forEach((el) => {
|
|
1999
|
+
el.hidden = !el.hidden;
|
|
2000
|
+
});
|
|
2001
|
+
const h = luxon.DateTime.fromFormat(`${hour}`, "H").toFormat("h");
|
|
2002
|
+
hour = luxon.DateTime.fromFormat(`${h}${ampm}`, "ha", { locale: "en-US" }).hour;
|
|
2003
|
+
}
|
|
2004
|
+
}
|
|
2005
|
+
var minute = 0;
|
|
2006
|
+
if (this.timePickerOpts.showMinutes) {
|
|
2007
|
+
minute = parseInt(time.querySelector(".minuteselect").value, 10);
|
|
2008
|
+
if (isNaN(minute))
|
|
2009
|
+
minute = parseInt(time.querySelector(".minuteselect option:last-child").value, 10);
|
|
2010
|
+
}
|
|
2011
|
+
var second = 0;
|
|
2012
|
+
if (this.timePickerOpts.showSeconds) {
|
|
2013
|
+
second = parseInt(time.querySelector(".secondselect").value, 10);
|
|
2014
|
+
if (isNaN(second))
|
|
2015
|
+
second = parseInt(time.querySelector(".secondselect option:last-child").value, 10);
|
|
2016
|
+
}
|
|
2017
|
+
if (side === "start") {
|
|
2018
|
+
if (this.#startDate)
|
|
2019
|
+
this.#startDate = this.#startDate.set({ hour, minute, second });
|
|
2020
|
+
if (this.singleDatePicker) {
|
|
2021
|
+
this.#endDate = this.#startDate;
|
|
2022
|
+
} else if (this.#endDate && this.#endDate.hasSame(this.#startDate, "day") && this.#endDate < this.#startDate) {
|
|
2023
|
+
this.#endDate = this.#startDate;
|
|
2024
|
+
}
|
|
2025
|
+
} else if (this.#endDate) {
|
|
2026
|
+
this.#endDate = this.#endDate.set({ hour, minute, second });
|
|
2027
|
+
}
|
|
2028
|
+
this.updateCalendars(false);
|
|
2029
|
+
this.setApplyBtnState();
|
|
2030
|
+
this.triggerEvent(this.#events.onBeforeRenderTimePicker);
|
|
2031
|
+
this.renderTimePicker("start");
|
|
2032
|
+
this.renderTimePicker("end");
|
|
2033
|
+
if (this.autoUpdateInput)
|
|
2034
|
+
this.updateElement();
|
|
2035
|
+
this.triggerEvent(this.#events.onTimeChange, { side: this.singleDatePicker ? null : side });
|
|
2036
|
+
}
|
|
2037
|
+
/**
|
|
2038
|
+
* Calender month moved
|
|
2039
|
+
* @param {external:Event} e - The Event target
|
|
2040
|
+
* @private
|
|
2041
|
+
*/
|
|
2042
|
+
monthOrYearChanged(e) {
|
|
2043
|
+
const isLeft = e.target.closest(".drp-calendar").classList.contains("left");
|
|
2044
|
+
const leftOrRight = isLeft ? "left" : "right";
|
|
2045
|
+
const cal = this.container.querySelector(`.drp-calendar.${leftOrRight}`);
|
|
2046
|
+
let month = parseInt(cal.querySelector(".monthselect").value, 10);
|
|
2047
|
+
let year = cal.querySelector(".yearselect").value;
|
|
2048
|
+
let monthChange = false;
|
|
2049
|
+
if (!isLeft) {
|
|
2050
|
+
if (year < this.#startDate.year || year == this.#startDate.year && month < this.#startDate.month) {
|
|
2051
|
+
month = this.#startDate.month;
|
|
2052
|
+
year = this.#startDate.year;
|
|
2053
|
+
}
|
|
2054
|
+
}
|
|
2055
|
+
if (this.minDate) {
|
|
2056
|
+
if (year < this.minDate.year || year == this.minDate.year && month < this.minDate.month) {
|
|
2057
|
+
month = this.minDate.month;
|
|
2058
|
+
year = this.minDate.year;
|
|
2059
|
+
}
|
|
2060
|
+
}
|
|
2061
|
+
if (this.maxDate) {
|
|
2062
|
+
if (year > this.maxDate.year || year == this.maxDate.year && month > this.maxDate.month) {
|
|
2063
|
+
month = this.maxDate.month;
|
|
2064
|
+
year = this.maxDate.year;
|
|
2065
|
+
}
|
|
2066
|
+
}
|
|
2067
|
+
if (isLeft) {
|
|
2068
|
+
monthChange = !luxon.DateTime.fromObject({ year, month }).hasSame(this.leftCalendar.month, "month");
|
|
2069
|
+
this.leftCalendar.month = this.leftCalendar.month.set({ year, month });
|
|
2070
|
+
if (this.linkedCalendars)
|
|
2071
|
+
this.rightCalendar.month = this.leftCalendar.month.plus({ month: 1 });
|
|
2072
|
+
} else {
|
|
2073
|
+
monthChange = !luxon.DateTime.fromObject({ year, month }).hasSame(this.leftCalendar.month, "month");
|
|
2074
|
+
this.rightCalendar.month = this.rightCalendar.month.set({ year, month });
|
|
2075
|
+
if (this.linkedCalendars)
|
|
2076
|
+
this.leftCalendar.month = this.rightCalendar.month.minus({ month: 1 });
|
|
2077
|
+
}
|
|
2078
|
+
this.updateCalendars(monthChange);
|
|
2079
|
+
}
|
|
2080
|
+
/**
|
|
2081
|
+
* User clicked `Apply` button
|
|
2082
|
+
* @emits "apply"
|
|
2083
|
+
* @private
|
|
2084
|
+
*/
|
|
2085
|
+
clickApply() {
|
|
2086
|
+
this.hide();
|
|
2087
|
+
this.triggerEvent(this.#events.onApply);
|
|
2088
|
+
}
|
|
2089
|
+
/**
|
|
2090
|
+
* User clicked `Cancel` button
|
|
2091
|
+
* @emits "cancel"
|
|
2092
|
+
* @private
|
|
2093
|
+
*/
|
|
2094
|
+
clickCancel() {
|
|
2095
|
+
this.#startDate = this.oldStartDate;
|
|
2096
|
+
this.#endDate = this.oldEndDate;
|
|
2097
|
+
this.hide();
|
|
2098
|
+
this.triggerEvent(this.#events.onCancel);
|
|
2099
|
+
}
|
|
2100
|
+
/* #endregion */
|
|
2101
|
+
/**
|
|
2102
|
+
* Update the picker with value from `<input>` element.<br>
|
|
2103
|
+
* Input values must be given in format of `locale.format`. Invalid values are handles by `violate` Event
|
|
2104
|
+
* @emits "inputChange"
|
|
2105
|
+
* @private
|
|
2106
|
+
*/
|
|
2107
|
+
elementChanged() {
|
|
2108
|
+
if (!this.isInputText) return;
|
|
2109
|
+
if (!this.element.value.length) return;
|
|
2110
|
+
const format = typeof this.locale.format === "string" ? this.locale.format : luxon.DateTime.parseFormatForOpts(this.locale.format);
|
|
2111
|
+
const dateString = this.element.value.split(this.locale.separator);
|
|
2112
|
+
let monthChange = false;
|
|
2113
|
+
if (this.singleDatePicker) {
|
|
2114
|
+
let newDate = luxon.DateTime.fromFormat(this.element.value, format, { locale: luxon.DateTime.now().locale });
|
|
2115
|
+
const oldDate = this.#startDate;
|
|
2116
|
+
if (!newDate.isValid || oldDate.equals(newDate))
|
|
2117
|
+
return;
|
|
2118
|
+
const violations = this.validateInput([newDate, null], true);
|
|
2119
|
+
if (violations != null) {
|
|
2120
|
+
if (violations.newDate != null) {
|
|
2121
|
+
newDate = violations.newDate.startDate;
|
|
2122
|
+
} else {
|
|
2123
|
+
return;
|
|
2124
|
+
}
|
|
2125
|
+
}
|
|
2126
|
+
monthChange = !this.#startDate.hasSame(newDate, "month");
|
|
2127
|
+
this.#startDate = newDate;
|
|
2128
|
+
this.#endDate = this.#startDate;
|
|
2129
|
+
if (!this.timePicker) {
|
|
2130
|
+
this.#startDate = this.#startDate.startOf("day");
|
|
2131
|
+
this.#endDate = this.#endDate.endOf("day");
|
|
2132
|
+
}
|
|
2133
|
+
} else if (!this.singleDatePicker && dateString.length === 2) {
|
|
2134
|
+
const newDate = [0, 1].map((i) => luxon.DateTime.fromFormat(dateString[i], format, { locale: luxon.DateTime.now().locale }));
|
|
2135
|
+
const oldDate = [this.#startDate, this.#endDate];
|
|
2136
|
+
if (!newDate[0].isValid || !newDate[1].isValid || (oldDate[0].equals(newDate[0]) && oldDate[1].equals(newDate[1]) || newDate[0] > newDate[1]))
|
|
2137
|
+
return;
|
|
2138
|
+
const violations = this.validateInput([newDate[0], newDate[1]], true);
|
|
2139
|
+
if (violations != null) {
|
|
2140
|
+
if (violations.newDate != null) {
|
|
2141
|
+
newDate[0] = violations.newDate.startDate;
|
|
2142
|
+
newDate[1] = violations.newDate.endDate;
|
|
2143
|
+
} else {
|
|
2144
|
+
return;
|
|
2145
|
+
}
|
|
2146
|
+
}
|
|
2147
|
+
monthChange = !this.#startDate.hasSame(newDate[0], "month") || !this.#endDate.hasSame(newDate[1], "month");
|
|
2148
|
+
this.#startDate = newDate[0];
|
|
2149
|
+
this.#endDate = newDate[1];
|
|
2150
|
+
if (!this.timePicker) {
|
|
2151
|
+
this.#startDate = this.#startDate.startOf("day");
|
|
2152
|
+
this.#endDate = this.#endDate.endOf("day");
|
|
2153
|
+
}
|
|
2154
|
+
} else {
|
|
2155
|
+
return;
|
|
2156
|
+
}
|
|
2157
|
+
this.updateView(monthChange);
|
|
2158
|
+
this.updateElement();
|
|
2159
|
+
this.triggerEvent(this.#events.onInputChange);
|
|
2160
|
+
}
|
|
2161
|
+
/**
|
|
2162
|
+
* Handles key press, IE 11 compatibility
|
|
2163
|
+
* @param {external:Event} e - The Event target
|
|
2164
|
+
* @private
|
|
2165
|
+
*/
|
|
2166
|
+
keydown(e) {
|
|
2167
|
+
if ([9, 11].includes(e.keyCode))
|
|
2168
|
+
this.hide();
|
|
2169
|
+
if (e.keyCode === 27) {
|
|
2170
|
+
e.preventDefault();
|
|
2171
|
+
e.stopPropagation();
|
|
2172
|
+
this.hide();
|
|
2173
|
+
}
|
|
2174
|
+
}
|
|
2175
|
+
/**
|
|
2176
|
+
* Update attached `<input>` element with selected value
|
|
2177
|
+
* @emits external:change
|
|
2178
|
+
*/
|
|
2179
|
+
updateElement() {
|
|
2180
|
+
if (this.#startDate == null && this.initalMonth)
|
|
2181
|
+
return;
|
|
2182
|
+
if (this.isInputText) {
|
|
2183
|
+
let newValue = this.formatDate(this.#startDate);
|
|
2184
|
+
if (!this.singleDatePicker) {
|
|
2185
|
+
newValue += this.locale.separator;
|
|
2186
|
+
if (this.#endDate)
|
|
2187
|
+
newValue += this.formatDate(this.#endDate);
|
|
2188
|
+
}
|
|
2189
|
+
this.updateAltInput();
|
|
2190
|
+
if (newValue !== this.element.value) {
|
|
2191
|
+
this.element.value = newValue;
|
|
2192
|
+
this.element.dispatchEvent(new Event("change", { bubbles: true }));
|
|
2193
|
+
}
|
|
2194
|
+
} else {
|
|
2195
|
+
this.updateAltInput();
|
|
2196
|
+
}
|
|
2197
|
+
}
|
|
2198
|
+
/**
|
|
2199
|
+
* Update altInput `<input>` element with selected value
|
|
2200
|
+
*/
|
|
2201
|
+
updateAltInput() {
|
|
2202
|
+
if (this.altInput == null)
|
|
2203
|
+
return;
|
|
2204
|
+
if (this.altFormat == null) {
|
|
2205
|
+
let precision = "day";
|
|
2206
|
+
if (this.timePicker) {
|
|
2207
|
+
if (this.timePickerOpts.showSeconds) {
|
|
2208
|
+
precision = "second";
|
|
2209
|
+
} else if (this.timePickerOpts.showMinutes) {
|
|
2210
|
+
precision = "minute";
|
|
2211
|
+
} else {
|
|
2212
|
+
precision = "hour";
|
|
2213
|
+
}
|
|
2214
|
+
}
|
|
2215
|
+
const startDate = this.#startDate.toISO({ format: "basic", precision, includeOffset: false });
|
|
2216
|
+
(this.singleDatePicker ? this.altInput : this.altInput[0]).value = startDate;
|
|
2217
|
+
if (!this.singleDatePicker && this.#endDate) {
|
|
2218
|
+
const endDate = this.#endDate.toISO({ format: "basic", precision, includeOffset: false });
|
|
2219
|
+
this.altInput[1].value = endDate;
|
|
2220
|
+
}
|
|
2221
|
+
} else {
|
|
2222
|
+
const startDate = typeof this.altFormat === "function" ? this.altFormat(this.#startDate) : this.formatDate(this.#startDate, this.altFormat);
|
|
2223
|
+
(this.singleDatePicker ? this.altInput : this.altInput[0]).value = startDate;
|
|
2224
|
+
if (!this.singleDatePicker && this.#endDate) {
|
|
2225
|
+
const endDate = typeof this.altFormat === "function" ? this.altFormat(this.#endDate) : this.formatDate(this.#endDate, this.altFormat);
|
|
2226
|
+
this.altInput[1].value = endDate;
|
|
2227
|
+
}
|
|
2228
|
+
}
|
|
2229
|
+
}
|
|
2230
|
+
/**
|
|
2231
|
+
* Removes the picker from document
|
|
2232
|
+
*/
|
|
2233
|
+
remove() {
|
|
2234
|
+
this.element.removeEventListener("click", this.#showProxy);
|
|
2235
|
+
this.element.removeEventListener("focus", this.#showProxy);
|
|
2236
|
+
this.element.removeEventListener("keyup", this.#elementChangedProxy);
|
|
2237
|
+
this.element.removeEventListener("keydown", this.#keydownProxy);
|
|
2238
|
+
this.element.removeEventListener("click", this.#toggleProxy);
|
|
2239
|
+
this.element.removeEventListener("keydown", this.#toggleProxy);
|
|
2240
|
+
this.container.remove();
|
|
2241
|
+
}
|
|
2242
|
+
/**
|
|
2243
|
+
* Helper function to dispatch events
|
|
2244
|
+
* @param {object} ev - Event template from this.#events
|
|
2245
|
+
* @param {...object} args - Additional parameters if needed
|
|
2246
|
+
*/
|
|
2247
|
+
triggerEvent(ev, ...args) {
|
|
2248
|
+
if (args.length === 0) {
|
|
2249
|
+
const event = new DateRangePickerEvent(this, ev);
|
|
2250
|
+
this.element.dispatchEvent(event);
|
|
2251
|
+
return event;
|
|
2252
|
+
} else {
|
|
2253
|
+
const event = new DateRangePickerEvent(this, ev, ...args);
|
|
2254
|
+
this.element.dispatchEvent(event);
|
|
2255
|
+
return event;
|
|
2256
|
+
}
|
|
2257
|
+
}
|
|
2258
|
+
/**
|
|
2259
|
+
* Helper function to add eventListener similar to jQuery .on( events [, selector ] [, data ] )
|
|
2260
|
+
* @param {string} element - Query selector of element where listener is added
|
|
2261
|
+
* @param {string} eventName - Name of the event
|
|
2262
|
+
* @param {string} selector - Query selector string to filter the descendants of the element
|
|
2263
|
+
* @param {function} delegate - Handler data
|
|
2264
|
+
* @private
|
|
2265
|
+
*/
|
|
2266
|
+
addListener(element, eventName, selector, delegate) {
|
|
2267
|
+
this.container.querySelectorAll(element).forEach((el) => {
|
|
2268
|
+
el.addEventListener(eventName, function(event) {
|
|
2269
|
+
const target = event.target.closest(selector);
|
|
2270
|
+
if (target && el.contains(target))
|
|
2271
|
+
delegate.call(target, event);
|
|
2272
|
+
});
|
|
2273
|
+
});
|
|
2274
|
+
}
|
|
2275
|
+
}
|
|
2276
|
+
function createElementFromHTML(html) {
|
|
2277
|
+
const template = document.createElement("template");
|
|
2278
|
+
template.innerHTML = html.trim();
|
|
2279
|
+
return template.content.firstElementChild;
|
|
2280
|
+
}
|
|
2281
|
+
class DateRangePickerEvent extends Event {
|
|
2282
|
+
#picker;
|
|
2283
|
+
constructor(drp, ev, args = {}) {
|
|
2284
|
+
let param = {};
|
|
2285
|
+
if (ev.param)
|
|
2286
|
+
param = typeof ev.param === "function" ? ev.param() : ev.param;
|
|
2287
|
+
param = { ...param, ...args };
|
|
2288
|
+
super(ev.type, param);
|
|
2289
|
+
this.#picker = drp;
|
|
2290
|
+
for (const [key2, value] of Object.entries(param)) {
|
|
2291
|
+
if (Object.getOwnPropertyNames(Event.prototype).includes(key2)) continue;
|
|
2292
|
+
this[key2] = value;
|
|
2293
|
+
}
|
|
2294
|
+
}
|
|
2295
|
+
get picker() {
|
|
2296
|
+
return this.#picker;
|
|
2297
|
+
}
|
|
2298
|
+
}
|
|
2299
|
+
function offset(el) {
|
|
2300
|
+
const rect = el.getBoundingClientRect();
|
|
2301
|
+
return {
|
|
2302
|
+
top: rect.top + window.scrollY,
|
|
2303
|
+
left: rect.left + window.scrollX
|
|
2304
|
+
};
|
|
2305
|
+
}
|
|
2306
|
+
function outerWidth(el, withMargin = false) {
|
|
2307
|
+
if (!withMargin)
|
|
2308
|
+
return el.offsetWidth;
|
|
2309
|
+
const style = getComputedStyle(el);
|
|
2310
|
+
return el.offsetWidth + parseFloat(style.marginLeft) + parseFloat(style.marginRight);
|
|
2311
|
+
}
|
|
2312
|
+
function outerHeight(el, withMargin = false) {
|
|
2313
|
+
if (!withMargin)
|
|
2314
|
+
return el.offsetHeight;
|
|
2315
|
+
const style = getComputedStyle(el);
|
|
2316
|
+
return el.offsetHeight + parseFloat(style.marginTop) + parseFloat(style.marginBottom);
|
|
2317
|
+
}
|
|
2318
|
+
function daterangepicker(elements, options, callback) {
|
|
2319
|
+
if (typeof elements === "string")
|
|
2320
|
+
return daterangepicker(document.querySelectorAll(elements), options, callback);
|
|
2321
|
+
if (elements instanceof HTMLElement)
|
|
2322
|
+
elements = [elements];
|
|
2323
|
+
if (elements instanceof NodeList || elements instanceof HTMLCollection)
|
|
2324
|
+
elements = Array.from(elements);
|
|
2325
|
+
if (elements == null)
|
|
2326
|
+
return new DateRangePicker(null, options || {}, callback);
|
|
2327
|
+
elements.forEach((el) => {
|
|
2328
|
+
if (el._daterangepicker && typeof el._daterangepicker.remove === "function")
|
|
2329
|
+
el._daterangepicker.remove();
|
|
2330
|
+
el._daterangepicker = new DateRangePicker(el, options || {}, callback);
|
|
2331
|
+
});
|
|
2332
|
+
return elements.length === 1 ? elements[0] : elements;
|
|
2333
|
+
}
|
|
2334
|
+
function getDateRangePicker(target) {
|
|
2335
|
+
if (typeof target === "string")
|
|
2336
|
+
target = document.querySelector(target);
|
|
2337
|
+
return target instanceof HTMLElement ? target._daterangepicker : void 0;
|
|
2338
|
+
}
|
|
2339
|
+
if (window.jQuery?.fn) {
|
|
2340
|
+
jQuery.fn.daterangepicker = function(options, callback) {
|
|
2341
|
+
return this.each(function() {
|
|
2342
|
+
daterangepicker(this, options, callback);
|
|
2343
|
+
});
|
|
2344
|
+
};
|
|
2345
|
+
}
|
|
2346
|
+
function registerJqueryPlugin(jq) {
|
|
2347
|
+
if (!jq?.fn) return;
|
|
2348
|
+
jq.fn.daterangepicker = function(options, callback) {
|
|
2349
|
+
return this.each(function() {
|
|
2350
|
+
daterangepicker(this, options, callback);
|
|
2351
|
+
});
|
|
2352
|
+
};
|
|
2353
|
+
}
|
|
2354
|
+
Object.defineProperty(window, "jQuery", {
|
|
2355
|
+
configurable: true,
|
|
2356
|
+
set(value) {
|
|
2357
|
+
this._jQuery = value;
|
|
2358
|
+
registerJqueryPlugin(value);
|
|
2359
|
+
},
|
|
2360
|
+
get() {
|
|
2361
|
+
return this._jQuery;
|
|
2362
|
+
}
|
|
2363
|
+
});
|
|
2364
|
+
exports.DateRangePicker = DateRangePicker;
|
|
2365
|
+
exports.daterangepicker = daterangepicker;
|
|
2366
|
+
exports.getDateRangePicker = getDateRangePicker;
|
|
2367
|
+
//# sourceMappingURL=daterangepicker.js.map
|