@lekoala/date-picker 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +281 -263
- package/custom-elements.json +30 -0
- package/dist/date-picker.css +51 -46
- package/dist/date-picker.js +685 -204
- package/dist/date-picker.min.css +1 -1
- package/dist/date-picker.min.js +13 -13
- package/dist/date-picker.standalone.min.js +65 -60
- package/dist/types/control-classes.d.ts +30 -0
- package/dist/types/control-classes.d.ts.map +1 -0
- package/dist/types/date-calendar.d.ts +49 -3
- package/dist/types/date-calendar.d.ts.map +1 -1
- package/dist/types/date-field.d.ts +12 -4
- package/dist/types/date-field.d.ts.map +1 -1
- package/dist/types/date-picker.d.ts +106 -9
- package/dist/types/date-picker.d.ts.map +1 -1
- package/dist/types/date-range.d.ts +102 -20
- package/dist/types/date-range.d.ts.map +1 -1
- package/dist/types/date.d.ts +14 -2
- package/dist/types/date.d.ts.map +1 -1
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/intl.d.ts.map +1 -1
- package/dist/types/source.d.ts +1 -0
- package/dist/types/source.d.ts.map +1 -1
- package/docs/ACCESSIBILITY.md +6 -2
- package/docs/API.md +430 -358
- package/docs/DECISIONS.md +68 -49
- package/docs/MIGRATION-FLATPICKR.md +1 -1
- package/docs/TESTING.md +1 -1
- package/docs/USE_CASES.md +156 -141
- package/package.json +8 -8
- package/src/control-classes.js +50 -0
- package/src/date-calendar.js +188 -54
- package/src/date-field.js +9 -5
- package/src/date-picker.css +51 -46
- package/src/date-picker.js +392 -83
- package/src/date-range.js +184 -30
- package/src/date.js +57 -3
- package/src/index.js +1 -0
- package/src/intl.js +52 -17
- package/src/source.js +14 -4
package/dist/date-picker.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*** @lekoala/date-picker v0.
|
|
1
|
+
/*** @lekoala/date-picker v0.5.0 - https://github.com/lekoala/date-picker ***/
|
|
2
2
|
(() => {
|
|
3
3
|
var __defProp = Object.defineProperty;
|
|
4
4
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
@@ -176,15 +176,48 @@
|
|
|
176
176
|
const end = endOfWeek(endOfMonth(value), firstDay);
|
|
177
177
|
const weeks = [];
|
|
178
178
|
let cursor = start;
|
|
179
|
-
|
|
179
|
+
for (;; ) {
|
|
180
180
|
const week = [];
|
|
181
181
|
for (let i = 0;i < 7; i++)
|
|
182
182
|
week.push(addDays(cursor, i));
|
|
183
183
|
weeks.push(week);
|
|
184
|
+
if (compareDates(week[6], end) >= 0) {
|
|
185
|
+
if (!options.fixedWeeks || weeks.length >= 6 || compareDates(week[6], addDays("9999-12-31", -7)) > 0)
|
|
186
|
+
break;
|
|
187
|
+
}
|
|
184
188
|
cursor = addDays(cursor, 7);
|
|
185
189
|
}
|
|
186
190
|
return weeks;
|
|
187
191
|
}
|
|
192
|
+
function dayOrdinal(year, month, day) {
|
|
193
|
+
const base = toUTCDate({ year: 1, month: 1, day: 1 });
|
|
194
|
+
return Math.round((toUTCDate({ year, month, day }).getTime() - base.getTime()) / 86400000);
|
|
195
|
+
}
|
|
196
|
+
function clampDisplayMonth(value, firstDay = 1) {
|
|
197
|
+
const normalized = normalizeFirstDay(firstDay);
|
|
198
|
+
const maxOrdinal = dayOrdinal(9999, 12, 31);
|
|
199
|
+
let key = monthKey(value);
|
|
200
|
+
for (;; ) {
|
|
201
|
+
const parsed = parseDate(`${key}-01`);
|
|
202
|
+
if (!parsed)
|
|
203
|
+
throw new TypeError(`Invalid civil date/month: ${value}`);
|
|
204
|
+
const startOrdinal = dayOrdinal(parsed.year, parsed.month, 1);
|
|
205
|
+
const startOffset = (toUTCDate(parsed).getUTCDay() - normalized + 7) % 7;
|
|
206
|
+
if (startOrdinal - startOffset < 0) {
|
|
207
|
+
key = shiftMonth(key, 1);
|
|
208
|
+
continue;
|
|
209
|
+
}
|
|
210
|
+
const lastDay = daysInMonth(parsed.year, parsed.month);
|
|
211
|
+
const endParts = { year: parsed.year, month: parsed.month, day: lastDay };
|
|
212
|
+
const endOrdinal = startOrdinal + lastDay - 1;
|
|
213
|
+
const endOffset = (toUTCDate(endParts).getUTCDay() - normalized + 7) % 7;
|
|
214
|
+
if (endOrdinal - endOffset + 6 > maxOrdinal) {
|
|
215
|
+
key = shiftMonth(key, -1);
|
|
216
|
+
continue;
|
|
217
|
+
}
|
|
218
|
+
return key;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
188
221
|
function clampDate(value, min = "", max = "") {
|
|
189
222
|
if (!isDate(value))
|
|
190
223
|
throw new TypeError(`Invalid civil date: ${value}`);
|
|
@@ -265,6 +298,30 @@
|
|
|
265
298
|
}
|
|
266
299
|
}
|
|
267
300
|
|
|
301
|
+
// src/control-classes.js
|
|
302
|
+
function normalizeControlClass(value) {
|
|
303
|
+
if (typeof value !== "string")
|
|
304
|
+
return "";
|
|
305
|
+
return [
|
|
306
|
+
...new Set(value.split(/\s+/).filter((token) => token && !token.startsWith("dp-") && ![...token].some((char) => char.charCodeAt(0) < 32 || char.charCodeAt(0) === 127)))
|
|
307
|
+
].join(" ");
|
|
308
|
+
}
|
|
309
|
+
function sameControlClasses(a, b) {
|
|
310
|
+
return a.month === b.month && a.year === b.year && a.nav === b.nav;
|
|
311
|
+
}
|
|
312
|
+
var defaults = { month: "", year: "", nav: "" };
|
|
313
|
+
function getDefaultControlClasses() {
|
|
314
|
+
return { ...defaults };
|
|
315
|
+
}
|
|
316
|
+
function resolveControlClasses(classes) {
|
|
317
|
+
const result = getDefaultControlClasses();
|
|
318
|
+
for (const role of ["month", "year", "nav"]) {
|
|
319
|
+
if (classes && Object.hasOwn(classes, role))
|
|
320
|
+
result[role] = normalizeControlClass(classes[role]);
|
|
321
|
+
}
|
|
322
|
+
return result;
|
|
323
|
+
}
|
|
324
|
+
|
|
268
325
|
// src/date-range.js
|
|
269
326
|
function normalizeRange(range) {
|
|
270
327
|
if (range == null)
|
|
@@ -312,39 +369,160 @@
|
|
|
312
369
|
focus(endpoint) {
|
|
313
370
|
this.activeEndpoint = endpoint;
|
|
314
371
|
}
|
|
315
|
-
|
|
372
|
+
_refuse(endpoint) {
|
|
373
|
+
return {
|
|
374
|
+
status: "refused",
|
|
375
|
+
endpoint,
|
|
376
|
+
changedEndpoints: [],
|
|
377
|
+
range: this.range,
|
|
378
|
+
activeEndpoint: this.activeEndpoint
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
project(date, editable = () => true) {
|
|
316
382
|
if (!isDate(date))
|
|
317
383
|
throw new TypeError(`Invalid range activation: ${date}`);
|
|
318
|
-
|
|
319
|
-
|
|
384
|
+
const { start, end } = this;
|
|
385
|
+
if (this.complete && (compareDates(date, start) < 0 || compareDates(date, end) > 0)) {
|
|
386
|
+
const endpoint = compareDates(date, start) < 0 ? "start" : "end";
|
|
320
387
|
if (!editable(endpoint))
|
|
321
|
-
return
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
388
|
+
return this._refuse(endpoint);
|
|
389
|
+
return {
|
|
390
|
+
status: "complete",
|
|
391
|
+
endpoint,
|
|
392
|
+
changedEndpoints: [endpoint],
|
|
393
|
+
range: endpoint === "start" ? { start: date, end } : { start, end: date },
|
|
394
|
+
activeEndpoint: endpoint
|
|
395
|
+
};
|
|
325
396
|
}
|
|
326
397
|
if (this.activeEndpoint === "end") {
|
|
327
|
-
if (
|
|
328
|
-
|
|
398
|
+
if (start && !end && compareDates(date, start) < 0) {
|
|
399
|
+
if (!editable("start"))
|
|
400
|
+
return this._refuse("start");
|
|
401
|
+
if (!editable("end"))
|
|
402
|
+
return this._refuse("end");
|
|
403
|
+
return {
|
|
404
|
+
status: "complete",
|
|
405
|
+
endpoint: "end",
|
|
406
|
+
changedEndpoints: ["start", "end"],
|
|
407
|
+
range: { start: date, end: start },
|
|
408
|
+
activeEndpoint: "end"
|
|
409
|
+
};
|
|
329
410
|
}
|
|
411
|
+
if (start && compareDates(date, start) < 0)
|
|
412
|
+
return this._refuse("end");
|
|
330
413
|
if (!editable("end"))
|
|
331
|
-
return
|
|
332
|
-
|
|
333
|
-
|
|
414
|
+
return this._refuse("end");
|
|
415
|
+
return {
|
|
416
|
+
status: start ? "complete" : "pending",
|
|
417
|
+
endpoint: "end",
|
|
418
|
+
changedEndpoints: ["end"],
|
|
419
|
+
range: { start, end: date },
|
|
420
|
+
activeEndpoint: "end"
|
|
421
|
+
};
|
|
334
422
|
}
|
|
335
|
-
if (!
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
423
|
+
if (end && !start && compareDates(date, end) > 0) {
|
|
424
|
+
if (!editable("start"))
|
|
425
|
+
return this._refuse("start");
|
|
426
|
+
if (!editable("end"))
|
|
427
|
+
return this._refuse("end");
|
|
428
|
+
return {
|
|
429
|
+
status: "complete",
|
|
430
|
+
endpoint: "start",
|
|
431
|
+
changedEndpoints: ["start", "end"],
|
|
432
|
+
range: { start: end, end: date },
|
|
433
|
+
activeEndpoint: "start"
|
|
434
|
+
};
|
|
340
435
|
}
|
|
341
|
-
|
|
342
|
-
|
|
436
|
+
if (!editable("start"))
|
|
437
|
+
return this._refuse("start");
|
|
438
|
+
const canContinue = editable("end");
|
|
439
|
+
return {
|
|
440
|
+
status: "pending",
|
|
441
|
+
endpoint: "start",
|
|
442
|
+
changedEndpoints: ["start"],
|
|
443
|
+
range: { start: date, end },
|
|
444
|
+
activeEndpoint: canContinue ? "end" : this.activeEndpoint,
|
|
445
|
+
...canContinue ? {} : { close: true }
|
|
446
|
+
};
|
|
447
|
+
}
|
|
448
|
+
projectEndpoint(date, endpoint, editable = () => true) {
|
|
449
|
+
if (!isDate(date))
|
|
450
|
+
throw new TypeError(`Invalid range activation: ${date}`);
|
|
451
|
+
if (!this.complete || !editable(endpoint))
|
|
452
|
+
return this._refuse(endpoint);
|
|
453
|
+
const { start, end } = this;
|
|
454
|
+
const previous = endpoint === "start" ? start : end;
|
|
455
|
+
const target = endpoint === "start" ? earlier(date, end) : later(date, start);
|
|
456
|
+
return {
|
|
457
|
+
status: "complete",
|
|
458
|
+
endpoint,
|
|
459
|
+
changedEndpoints: target === previous ? [] : [endpoint],
|
|
460
|
+
range: endpoint === "start" ? { start: target, end } : { start, end: target },
|
|
461
|
+
activeEndpoint: endpoint
|
|
462
|
+
};
|
|
463
|
+
}
|
|
464
|
+
previewRange(date, endpoint = "", editable = () => true) {
|
|
465
|
+
if (!isDate(date))
|
|
466
|
+
return null;
|
|
467
|
+
if (!endpoint && Boolean(this.start) === Boolean(this.end))
|
|
468
|
+
return null;
|
|
469
|
+
const transition = endpoint ? this.projectEndpoint(date, endpoint, editable) : this.project(date, editable);
|
|
470
|
+
if (transition.status === "refused")
|
|
471
|
+
return null;
|
|
472
|
+
const { start, end } = transition.range;
|
|
473
|
+
if (!start || end && compareDates(end, start) < 0)
|
|
474
|
+
return null;
|
|
475
|
+
return { start, end };
|
|
476
|
+
}
|
|
477
|
+
_apply(transition) {
|
|
478
|
+
if (transition.status === "refused")
|
|
479
|
+
return transition;
|
|
480
|
+
this.start = transition.range.start;
|
|
481
|
+
this.end = transition.range.end;
|
|
482
|
+
this.activeEndpoint = transition.activeEndpoint;
|
|
483
|
+
return transition;
|
|
484
|
+
}
|
|
485
|
+
activate(date, editable = () => true) {
|
|
486
|
+
return this._apply(this.project(date, editable));
|
|
487
|
+
}
|
|
488
|
+
moveEndpoint(date, endpoint, editable = () => true) {
|
|
489
|
+
return this._apply(this.projectEndpoint(date, endpoint, editable));
|
|
343
490
|
}
|
|
344
491
|
}
|
|
492
|
+
function later(a, b) {
|
|
493
|
+
if (!a)
|
|
494
|
+
return b;
|
|
495
|
+
if (!b)
|
|
496
|
+
return a;
|
|
497
|
+
return compareDates(a, b) >= 0 ? a : b;
|
|
498
|
+
}
|
|
499
|
+
function earlier(a, b) {
|
|
500
|
+
if (!a)
|
|
501
|
+
return b;
|
|
502
|
+
if (!b)
|
|
503
|
+
return a;
|
|
504
|
+
return compareDates(a, b) <= 0 ? a : b;
|
|
505
|
+
}
|
|
345
506
|
|
|
346
507
|
// src/intl.js
|
|
347
508
|
var BIDI = /[\u200e\u200f\u061c\u202a-\u202e\u2066-\u2069]/g;
|
|
509
|
+
var dateFormatters = new Map;
|
|
510
|
+
var digitMaps = new Map;
|
|
511
|
+
function cached(cache, key, create) {
|
|
512
|
+
const existing = cache.get(key);
|
|
513
|
+
if (existing !== undefined)
|
|
514
|
+
return existing;
|
|
515
|
+
const value = create();
|
|
516
|
+
const oldest = cache.keys().next().value;
|
|
517
|
+
if (cache.size >= 64 && oldest !== undefined)
|
|
518
|
+
cache.delete(oldest);
|
|
519
|
+
cache.set(key, value);
|
|
520
|
+
return value;
|
|
521
|
+
}
|
|
522
|
+
function dateFormatter(locale, options) {
|
|
523
|
+
const key = JSON.stringify([locale, options]);
|
|
524
|
+
return cached(dateFormatters, key, () => new Intl.DateTimeFormat(locale, options));
|
|
525
|
+
}
|
|
348
526
|
function resolveLocale(locale = "") {
|
|
349
527
|
if (locale)
|
|
350
528
|
return locale;
|
|
@@ -355,21 +533,25 @@
|
|
|
355
533
|
return "en-US";
|
|
356
534
|
}
|
|
357
535
|
function digitMap(locale) {
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
536
|
+
return cached(digitMaps, locale, () => {
|
|
537
|
+
const formatter = new Intl.NumberFormat(locale, { useGrouping: false });
|
|
538
|
+
const map = new Map;
|
|
539
|
+
for (let i = 0;i <= 9; i++)
|
|
540
|
+
map.set(formatter.format(i), String(i));
|
|
541
|
+
return map;
|
|
542
|
+
});
|
|
363
543
|
}
|
|
364
|
-
function normalizeDigits(text, locale) {
|
|
365
|
-
let result = String(text || "").replace(BIDI, "")
|
|
544
|
+
function normalizeDigits(text, locale, options = {}) {
|
|
545
|
+
let result = String(text || "").replace(BIDI, "");
|
|
546
|
+
if (options.trim !== false)
|
|
547
|
+
result = result.trim();
|
|
366
548
|
for (const [local, ascii] of digitMap(locale))
|
|
367
549
|
result = result.split(local).join(ascii);
|
|
368
550
|
return result;
|
|
369
551
|
}
|
|
370
552
|
function formatLongDate(value, locale = "") {
|
|
371
553
|
const resolved = resolveLocale(locale);
|
|
372
|
-
return
|
|
554
|
+
return dateFormatter(resolved, {
|
|
373
555
|
calendar: "gregory",
|
|
374
556
|
weekday: "long",
|
|
375
557
|
year: "numeric",
|
|
@@ -380,7 +562,7 @@
|
|
|
380
562
|
}
|
|
381
563
|
function formatMonthYear(value, locale = "") {
|
|
382
564
|
const resolved = resolveLocale(locale);
|
|
383
|
-
return
|
|
565
|
+
return dateFormatter(resolved, {
|
|
384
566
|
calendar: "gregory",
|
|
385
567
|
year: "numeric",
|
|
386
568
|
month: "long",
|
|
@@ -390,7 +572,7 @@
|
|
|
390
572
|
function monthYearOrder(locale = "") {
|
|
391
573
|
const resolved = resolveLocale(locale);
|
|
392
574
|
try {
|
|
393
|
-
const parts =
|
|
575
|
+
const parts = dateFormatter(resolved, {
|
|
394
576
|
calendar: "gregory",
|
|
395
577
|
year: "numeric",
|
|
396
578
|
month: "long",
|
|
@@ -415,12 +597,12 @@
|
|
|
415
597
|
}
|
|
416
598
|
function monthNames(locale = "", style = "long") {
|
|
417
599
|
const resolved = resolveLocale(locale);
|
|
418
|
-
const formatter =
|
|
600
|
+
const formatter = dateFormatter(resolved, { calendar: "gregory", month: style, timeZone: "UTC" });
|
|
419
601
|
return Array.from({ length: 12 }, (_, index) => formatter.format(toIntlDate(`2026-${String(index + 1).padStart(2, "0")}-15`)));
|
|
420
602
|
}
|
|
421
603
|
function weekdayNames(locale = "", firstDay = 1, style = "short") {
|
|
422
604
|
const resolved = resolveLocale(locale);
|
|
423
|
-
const formatter =
|
|
605
|
+
const formatter = dateFormatter(resolved, {
|
|
424
606
|
calendar: "gregory",
|
|
425
607
|
weekday: style,
|
|
426
608
|
timeZone: "UTC"
|
|
@@ -438,7 +620,7 @@
|
|
|
438
620
|
}
|
|
439
621
|
function createDateAdapter(locale = "") {
|
|
440
622
|
const resolved = resolveLocale(locale);
|
|
441
|
-
const formatter =
|
|
623
|
+
const formatter = dateFormatter(resolved, {
|
|
442
624
|
calendar: "gregory",
|
|
443
625
|
year: "numeric",
|
|
444
626
|
month: "2-digit",
|
|
@@ -462,8 +644,8 @@
|
|
|
462
644
|
return "(\\d{1,2})";
|
|
463
645
|
if (part.type === "year")
|
|
464
646
|
return "(\\d{4})";
|
|
465
|
-
const literal = normalizeDigits(part.value, resolved
|
|
466
|
-
return escapeRegExp(literal).replace(
|
|
647
|
+
const literal = normalizeDigits(part.value, resolved, { trim: false });
|
|
648
|
+
return escapeRegExp(literal).replace(/\s+/g, "\\s*");
|
|
467
649
|
}).join("");
|
|
468
650
|
const regex = new RegExp(`^\\s*${pattern}\\s*$`);
|
|
469
651
|
return {
|
|
@@ -517,9 +699,9 @@
|
|
|
517
699
|
rangeOrderStart: "Start must be on or before end",
|
|
518
700
|
rangeOrderEnd: "End must be on or after start"
|
|
519
701
|
};
|
|
520
|
-
var
|
|
702
|
+
var defaults2 = { ...DEFAULT_MESSAGES };
|
|
521
703
|
function getDefaultMessages() {
|
|
522
|
-
return { ...
|
|
704
|
+
return { ...defaults2 };
|
|
523
705
|
}
|
|
524
706
|
|
|
525
707
|
// src/source.js
|
|
@@ -557,19 +739,24 @@
|
|
|
557
739
|
function yearOf(month) {
|
|
558
740
|
return Number(month.slice(0, 4));
|
|
559
741
|
}
|
|
742
|
+
function stepMonthKey(month, amount) {
|
|
743
|
+
const [year, index] = month.split("-").map(Number);
|
|
744
|
+
const absolute = year * 12 + (index - 1) + amount;
|
|
745
|
+
const stepYear = Math.floor(absolute / 12);
|
|
746
|
+
const stepMonth = (absolute % 12 + 12) % 12 + 1;
|
|
747
|
+
if (stepYear < 1 || stepYear > 9999)
|
|
748
|
+
return null;
|
|
749
|
+
return `${String(stepYear).padStart(4, "0")}-${String(stepMonth).padStart(2, "0")}`;
|
|
750
|
+
}
|
|
751
|
+
function rangeAttributeMap(position) {
|
|
752
|
+
return {
|
|
753
|
+
"data-range-start": position === "start" || position === "single",
|
|
754
|
+
"data-range-end": position === "end" || position === "single",
|
|
755
|
+
"data-in-range": position === "in"
|
|
756
|
+
};
|
|
757
|
+
}
|
|
560
758
|
function rangeAttributes(position) {
|
|
561
|
-
|
|
562
|
-
case "single":
|
|
563
|
-
return ' data-range-start="true" data-range-end="true"';
|
|
564
|
-
case "start":
|
|
565
|
-
return ' data-range-start="true"';
|
|
566
|
-
case "end":
|
|
567
|
-
return ' data-range-end="true"';
|
|
568
|
-
case "in":
|
|
569
|
-
return ' data-in-range="true"';
|
|
570
|
-
default:
|
|
571
|
-
return "";
|
|
572
|
-
}
|
|
759
|
+
return Object.entries(rangeAttributeMap(position)).filter(([, on]) => on).map(([name]) => ` ${name}="true"`).join("");
|
|
573
760
|
}
|
|
574
761
|
function rangeLabelKey(position) {
|
|
575
762
|
if (position === "single")
|
|
@@ -612,7 +799,11 @@
|
|
|
612
799
|
this._renderDay = null;
|
|
613
800
|
this._isDateDisabled = null;
|
|
614
801
|
this._highlightedRange = { start: "", end: "" };
|
|
802
|
+
this._paintedRange = { start: "", end: "" };
|
|
803
|
+
this._paintedPreview = false;
|
|
804
|
+
this._renderedStates = new Map;
|
|
615
805
|
this._messages = getDefaultMessages();
|
|
806
|
+
this._controlClasses = getDefaultControlClasses();
|
|
616
807
|
this._onClick = this._onClick.bind(this);
|
|
617
808
|
this._onChange = this._onChange.bind(this);
|
|
618
809
|
this._onKeyDown = this._onKeyDown.bind(this);
|
|
@@ -730,11 +921,12 @@
|
|
|
730
921
|
}
|
|
731
922
|
set highlightedRange(value) {
|
|
732
923
|
const next = normalizeRange(value);
|
|
733
|
-
|
|
924
|
+
const preview = this.hasAttribute("data-range-preview");
|
|
925
|
+
if (next.start === this._paintedRange.start && next.end === this._paintedRange.end && preview === this._paintedPreview)
|
|
734
926
|
return;
|
|
735
927
|
this._highlightedRange = next;
|
|
736
928
|
if (this._connected)
|
|
737
|
-
this.
|
|
929
|
+
this._updateRangeDom();
|
|
738
930
|
}
|
|
739
931
|
get fixedWeeks() {
|
|
740
932
|
return this.hasAttribute("fixed-weeks");
|
|
@@ -748,6 +940,17 @@
|
|
|
748
940
|
set showWeekNumbers(value) {
|
|
749
941
|
this.toggleAttribute("show-week-numbers", Boolean(value));
|
|
750
942
|
}
|
|
943
|
+
get controlClasses() {
|
|
944
|
+
return { ...this._controlClasses };
|
|
945
|
+
}
|
|
946
|
+
set controlClasses(value) {
|
|
947
|
+
const classes = resolveControlClasses(value);
|
|
948
|
+
if (sameControlClasses(classes, this._controlClasses))
|
|
949
|
+
return;
|
|
950
|
+
this._controlClasses = classes;
|
|
951
|
+
if (this._connected)
|
|
952
|
+
this.render();
|
|
953
|
+
}
|
|
751
954
|
get messages() {
|
|
752
955
|
return this._messages;
|
|
753
956
|
}
|
|
@@ -796,7 +999,7 @@
|
|
|
796
999
|
const attrDisplay = this.getAttribute("display") || "";
|
|
797
1000
|
if (attrDisplay) {
|
|
798
1001
|
try {
|
|
799
|
-
this._model.setDisplay(attrDisplay);
|
|
1002
|
+
this._model.setDisplay(clampDisplayMonth(attrDisplay, firstDay));
|
|
800
1003
|
} catch {}
|
|
801
1004
|
} else if (this._model.value) {
|
|
802
1005
|
this._model.display = monthKey(this._model.value);
|
|
@@ -817,21 +1020,21 @@
|
|
|
817
1020
|
this._rendering = false;
|
|
818
1021
|
}
|
|
819
1022
|
_clamp(value) {
|
|
820
|
-
|
|
1023
|
+
const bounded = clampDate(value, this.min, this.max);
|
|
1024
|
+
const month = monthKey(bounded);
|
|
1025
|
+
const safeMonth = clampDisplayMonth(month, this.firstDay);
|
|
1026
|
+
if (safeMonth === month)
|
|
1027
|
+
return bounded;
|
|
1028
|
+
const day = Number(bounded.slice(8, 10)) || 1;
|
|
1029
|
+
const candidate = addDays(`${safeMonth}-01`, day - 1);
|
|
1030
|
+
const last = endOfMonth(safeMonth);
|
|
1031
|
+
return compareDates(candidate, last) > 0 ? last : candidate;
|
|
821
1032
|
}
|
|
822
1033
|
_weeks(display = this.display) {
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
while (weeks.length < 6 && cursor) {
|
|
828
|
-
const week = [];
|
|
829
|
-
for (let i = 1;i <= 7; i++)
|
|
830
|
-
week.push(addDays(cursor, i));
|
|
831
|
-
weeks.push(week);
|
|
832
|
-
cursor = week[6];
|
|
833
|
-
}
|
|
834
|
-
return weeks;
|
|
1034
|
+
return getMonthWeeks(clampDisplayMonth(display, this.firstDay), {
|
|
1035
|
+
firstDay: this.firstDay,
|
|
1036
|
+
fixedWeeks: this.fixedWeeks
|
|
1037
|
+
});
|
|
835
1038
|
}
|
|
836
1039
|
_focusTargetKey(element) {
|
|
837
1040
|
if (element.matches(".dp-month-select, .dp-year-input") && element.id) {
|
|
@@ -934,7 +1137,7 @@
|
|
|
934
1137
|
}
|
|
935
1138
|
_setDisplay(display, options = {}) {
|
|
936
1139
|
const previous = this._model.display;
|
|
937
|
-
this._model.setDisplay(display);
|
|
1140
|
+
this._model.setDisplay(clampDisplayMonth(display, this.firstDay));
|
|
938
1141
|
this._model.focused = this._clamp(this._model.focused);
|
|
939
1142
|
this._model.display = monthKey(this._model.focused);
|
|
940
1143
|
this._reflect("display", this._model.display);
|
|
@@ -952,10 +1155,10 @@
|
|
|
952
1155
|
}));
|
|
953
1156
|
}
|
|
954
1157
|
previousMonth() {
|
|
955
|
-
return this._setDisplay(
|
|
1158
|
+
return this._setDisplay(stepMonthKey(this.display, -1) ?? this.display);
|
|
956
1159
|
}
|
|
957
1160
|
nextMonth() {
|
|
958
|
-
return this._setDisplay(
|
|
1161
|
+
return this._setDisplay(stepMonthKey(this.display, 1) ?? this.display);
|
|
959
1162
|
}
|
|
960
1163
|
focusDate(date, options = {}) {
|
|
961
1164
|
if (!isDate(date))
|
|
@@ -1007,12 +1210,48 @@
|
|
|
1007
1210
|
}
|
|
1008
1211
|
return false;
|
|
1009
1212
|
}
|
|
1213
|
+
_dayLabel(date, state, position) {
|
|
1214
|
+
const rangeLabel = this.hasAttribute("data-range-preview") ? "" : rangeLabelKey(position);
|
|
1215
|
+
const description = typeof state.description === "string" ? state.description : "";
|
|
1216
|
+
return [
|
|
1217
|
+
formatLongDate(date, this.locale),
|
|
1218
|
+
description,
|
|
1219
|
+
rangeLabel ? this._messages[rangeLabel] : "",
|
|
1220
|
+
state.disabled ? this._messages.unavailable : ""
|
|
1221
|
+
].filter(Boolean).join(". ");
|
|
1222
|
+
}
|
|
1223
|
+
_updateRangeDom() {
|
|
1224
|
+
const previousRange = this._paintedRange;
|
|
1225
|
+
const previousPreview = this._paintedPreview;
|
|
1226
|
+
this._paintedRange = { ...this._highlightedRange };
|
|
1227
|
+
this._paintedPreview = this.hasAttribute("data-range-preview");
|
|
1228
|
+
for (const cell of this.querySelectorAll(".dp-day[data-date]")) {
|
|
1229
|
+
if (!(cell instanceof HTMLTableCellElement))
|
|
1230
|
+
continue;
|
|
1231
|
+
const date = cell.dataset.date || "";
|
|
1232
|
+
const position = rangePosition(date, this._highlightedRange);
|
|
1233
|
+
if (position === rangePosition(date, previousRange) && previousPreview === this._paintedPreview)
|
|
1234
|
+
continue;
|
|
1235
|
+
for (const [name, on] of Object.entries(rangeAttributeMap(position))) {
|
|
1236
|
+
if (on)
|
|
1237
|
+
cell.setAttribute(name, "true");
|
|
1238
|
+
else
|
|
1239
|
+
cell.removeAttribute(name);
|
|
1240
|
+
}
|
|
1241
|
+
if (!previousPreview || !this._paintedPreview) {
|
|
1242
|
+
const state = this._renderedStates.get(date) || {};
|
|
1243
|
+
const label = this._dayLabel(date, state, position);
|
|
1244
|
+
if (cell.getAttribute("aria-label") !== label)
|
|
1245
|
+
cell.setAttribute("aria-label", label);
|
|
1246
|
+
}
|
|
1247
|
+
}
|
|
1248
|
+
}
|
|
1010
1249
|
focusGrid() {
|
|
1011
1250
|
const target = this.querySelector(`.dp-day[data-date="${CSS.escape(this.focusedDate)}"]`);
|
|
1012
1251
|
if (target instanceof HTMLElement)
|
|
1013
1252
|
target.focus();
|
|
1014
1253
|
}
|
|
1015
|
-
async
|
|
1254
|
+
async activateDate(date) {
|
|
1016
1255
|
const confirmed = await this.ensureDate(date);
|
|
1017
1256
|
const state = this.getDateState(date);
|
|
1018
1257
|
if (!confirmed || state.disabled) {
|
|
@@ -1068,7 +1307,7 @@
|
|
|
1068
1307
|
this._reflect("display", this._model.display);
|
|
1069
1308
|
if (previousDisplay !== this.display)
|
|
1070
1309
|
this._emitDisplayChange();
|
|
1071
|
-
this.
|
|
1310
|
+
this.activateDate(date);
|
|
1072
1311
|
}
|
|
1073
1312
|
_onChange(event) {
|
|
1074
1313
|
const target = event.target;
|
|
@@ -1078,7 +1317,7 @@
|
|
|
1078
1317
|
}
|
|
1079
1318
|
if (target instanceof HTMLInputElement && target.matches(".dp-year-input")) {
|
|
1080
1319
|
const year = Number(target.value);
|
|
1081
|
-
if (!Number.isInteger(year) || year < 1 || year >
|
|
1320
|
+
if (!Number.isInteger(year) || year < 1 || year > 9998) {
|
|
1082
1321
|
if (this._connected)
|
|
1083
1322
|
this.render(false);
|
|
1084
1323
|
return;
|
|
@@ -1091,8 +1330,10 @@
|
|
|
1091
1330
|
if (!(target instanceof HTMLTableCellElement) || !target.matches(".dp-day[data-date]"))
|
|
1092
1331
|
return;
|
|
1093
1332
|
const date = target.dataset.date || "";
|
|
1094
|
-
if (isDate(date))
|
|
1095
|
-
|
|
1333
|
+
if (!isDate(date))
|
|
1334
|
+
return;
|
|
1335
|
+
this._model.focused = date;
|
|
1336
|
+
this.dispatchEvent(new CustomEvent("datefocus", { detail: { date, state: this.getDateState(date) }, bubbles: true }));
|
|
1096
1337
|
}
|
|
1097
1338
|
_onKeyDown(event) {
|
|
1098
1339
|
const target = event.target;
|
|
@@ -1101,7 +1342,7 @@
|
|
|
1101
1342
|
const date = target.dataset.date || this.focusedDate;
|
|
1102
1343
|
if (event.key === "Enter" || event.key === " ") {
|
|
1103
1344
|
event.preventDefault();
|
|
1104
|
-
this.
|
|
1345
|
+
this.activateDate(date);
|
|
1105
1346
|
return;
|
|
1106
1347
|
}
|
|
1107
1348
|
const anchorFocused = this._model.focused;
|
|
@@ -1150,8 +1391,10 @@
|
|
|
1150
1391
|
const gridId = `${this._id}-grid`;
|
|
1151
1392
|
const minMonth = this.min ? monthKey(this.min) : "";
|
|
1152
1393
|
const maxMonth = this.max ? monthKey(this.max) : "";
|
|
1153
|
-
const
|
|
1154
|
-
const
|
|
1394
|
+
const prevKey = stepMonthKey(display, -1);
|
|
1395
|
+
const nextKey = stepMonthKey(display, 1);
|
|
1396
|
+
const prevDisabled = Boolean(minMonth && prevKey !== null && prevKey < minMonth);
|
|
1397
|
+
const nextDisabled = Boolean(maxMonth && nextKey !== null && nextKey > maxMonth);
|
|
1155
1398
|
const minYearAttr = this.min ? ` min="${Number(this.min.slice(0, 4))}"` : "";
|
|
1156
1399
|
const maxYearAttr = this.max ? ` max="${Number(this.max.slice(0, 4))}"` : "";
|
|
1157
1400
|
const showWeeks = this.showWeekNumbers && this.firstDay === 1;
|
|
@@ -1163,23 +1406,18 @@
|
|
|
1163
1406
|
return `<option value="${String(month).padStart(2, "0")}"${month === displayMonth ? " selected" : ""}${disabled ? " disabled" : ""}>${escapeHtml(name)}</option>`;
|
|
1164
1407
|
}).join("");
|
|
1165
1408
|
const dayHeaders = weekdayShort.map((name, index) => `<th scope="col" abbr="${escapeHtml(weekdayLong[index])}">${escapeHtml(name.replace(".", ""))}</th>`).join("");
|
|
1409
|
+
this._renderedStates.clear();
|
|
1166
1410
|
const rows = weeks.map((week) => {
|
|
1167
1411
|
const weekNumber = showWeeks ? `<th scope="row" class="dp-week-number">${isoWeekNumber(week[0])}</th>` : "";
|
|
1168
1412
|
const cells = week.map((date) => {
|
|
1169
1413
|
const state = this.getDateState(date);
|
|
1414
|
+
this._renderedStates.set(date, state);
|
|
1170
1415
|
const outside = monthKey(date) !== display;
|
|
1171
1416
|
const selected = this.value === date;
|
|
1172
1417
|
const focused = this.focusedDate === date;
|
|
1173
1418
|
const isToday = date === today;
|
|
1174
1419
|
const position = rangePosition(date, this._highlightedRange);
|
|
1175
|
-
const
|
|
1176
|
-
const description = typeof state.description === "string" ? state.description : "";
|
|
1177
|
-
const label = [
|
|
1178
|
-
formatLongDate(date, locale),
|
|
1179
|
-
description,
|
|
1180
|
-
rangeLabel ? this._messages[rangeLabel] : "",
|
|
1181
|
-
state.disabled ? this._messages.unavailable : ""
|
|
1182
|
-
].filter(Boolean).join(". ");
|
|
1420
|
+
const label = this._dayLabel(date, state, position);
|
|
1183
1421
|
return `<td class="dp-day" data-date="${date}"${outside ? ' data-outside-month="true"' : ""}${isToday ? ' data-today="true" aria-current="date"' : ""}${selected ? ' data-selected="true" aria-selected="true"' : ""}${rangeAttributes(position)}${state.disabled ? ' data-disabled="true" aria-disabled="true"' : ""} tabindex="${focused ? "0" : "-1"}" aria-label="${escapeHtml(label)}"><span class="dp-day-number" aria-hidden="true">${Number(date.slice(8, 10))}</span><span class="dp-day-extra" aria-hidden="true"></span></td>`;
|
|
1184
1422
|
}).join("");
|
|
1185
1423
|
return `<tr>${weekNumber}${cells}</tr>`;
|
|
@@ -1188,18 +1426,22 @@
|
|
|
1188
1426
|
const focusKey = focusedElement instanceof HTMLElement && this.contains(focusedElement) ? this._focusTargetKey(focusedElement) : "";
|
|
1189
1427
|
const yearFirst = monthYearOrder(locale)[0] === "year";
|
|
1190
1428
|
this.toggleAttribute("data-year-first", yearFirst);
|
|
1429
|
+
const classes = this._controlClasses;
|
|
1430
|
+
const monthClass = escapeHtml(classes.month || "dp-skin");
|
|
1431
|
+
const yearClass = escapeHtml(classes.year || "dp-skin");
|
|
1432
|
+
const navClass = escapeHtml(classes.nav || "dp-skin");
|
|
1191
1433
|
const monthControl = `<label class="dp-visually-hidden" for="${this._id}-month">${escapeHtml(this._messages.month)}</label>
|
|
1192
|
-
<select id="${this._id}-month" class="dp-month-select" aria-label="${escapeHtml(this._messages.month)}">${monthOptions}</select>`;
|
|
1434
|
+
<select id="${this._id}-month" class="dp-month-select ${monthClass}" aria-label="${escapeHtml(this._messages.month)}">${monthOptions}</select>`;
|
|
1193
1435
|
const yearControl = `<label class="dp-visually-hidden" for="${this._id}-year">${escapeHtml(this._messages.year)}</label>
|
|
1194
|
-
<input id="${this._id}-year" class="dp-year-input" type="number" inputmode="numeric" value="${displayYear}"${minYearAttr}${maxYearAttr} aria-label="${escapeHtml(this._messages.year)}">`;
|
|
1436
|
+
<input id="${this._id}-year" class="dp-year-input ${yearClass}" type="number" inputmode="numeric" value="${displayYear}"${minYearAttr}${maxYearAttr} aria-label="${escapeHtml(this._messages.year)}">`;
|
|
1195
1437
|
this._rendering = true;
|
|
1196
1438
|
this.innerHTML = `
|
|
1197
1439
|
<div class="dp-calendar-shell">
|
|
1198
1440
|
<div class="dp-calendar-header">
|
|
1199
1441
|
${yearFirst ? yearControl : monthControl}
|
|
1200
1442
|
${yearFirst ? monthControl : yearControl}
|
|
1201
|
-
<button type="button" class="dp-nav dp-prev" data-calendar-action="previous" aria-label="${escapeHtml(this._messages.previousMonth)}"${prevDisabled ? " disabled" : ""}><svg aria-hidden="true" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="
|
|
1202
|
-
<button type="button" class="dp-nav dp-next" data-calendar-action="next" aria-label="${escapeHtml(this._messages.nextMonth)}"${nextDisabled ? " disabled" : ""}><svg aria-hidden="true" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="
|
|
1443
|
+
<button type="button" class="dp-nav dp-prev ${navClass}" data-calendar-action="previous" aria-label="${escapeHtml(this._messages.previousMonth)}"${prevDisabled ? " disabled" : ""}><svg aria-hidden="true" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="m4 10 4-4 4 4"/></svg></button>
|
|
1444
|
+
<button type="button" class="dp-nav dp-next ${navClass}" data-calendar-action="next" aria-label="${escapeHtml(this._messages.nextMonth)}"${nextDisabled ? " disabled" : ""}><svg aria-hidden="true" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="m4 6 4 4 4-4"/></svg></button>
|
|
1203
1445
|
</div>
|
|
1204
1446
|
<h2 id="${headingId}" class="dp-calendar-heading dp-visually-hidden" aria-live="polite">${escapeHtml(formatMonthYear(`${display}-15`, locale))}</h2>
|
|
1205
1447
|
<table id="${gridId}" class="dp-grid" role="grid" aria-labelledby="${headingId}">
|
|
@@ -1208,6 +1450,8 @@
|
|
|
1208
1450
|
</table>
|
|
1209
1451
|
</div>`;
|
|
1210
1452
|
this._rendering = false;
|
|
1453
|
+
this._paintedRange = { ...this._highlightedRange };
|
|
1454
|
+
this._paintedPreview = this.hasAttribute("data-range-preview");
|
|
1211
1455
|
if (focusKey) {
|
|
1212
1456
|
queueMicrotask(() => {
|
|
1213
1457
|
let selector = focusKey;
|
|
@@ -1227,7 +1471,7 @@
|
|
|
1227
1471
|
const extra = cell.querySelector(".dp-day-extra");
|
|
1228
1472
|
if (!(extra instanceof HTMLElement))
|
|
1229
1473
|
continue;
|
|
1230
|
-
const content = this._renderDay(date, this.
|
|
1474
|
+
const content = this._renderDay(date, this._renderedStates.get(date) || {});
|
|
1231
1475
|
if (content instanceof Node)
|
|
1232
1476
|
extra.append(content);
|
|
1233
1477
|
else if (content !== undefined && content !== null)
|
|
@@ -1243,13 +1487,15 @@
|
|
|
1243
1487
|
function crossAxisFor(side) {
|
|
1244
1488
|
return side === "top" || side === "bottom" ? "x" : "y";
|
|
1245
1489
|
}
|
|
1490
|
+
var OPPOSITE = { top: "bottom", bottom: "top", left: "right", right: "left" };
|
|
1491
|
+
var PLACEMENTS = new Set(Object.keys(OPPOSITE).flatMap((side) => [side, `${side}-start`, `${side}-end`]));
|
|
1492
|
+
var COORDINATE_SPACES = new Set(["viewport", "document"]);
|
|
1246
1493
|
function parsePlacement(placement) {
|
|
1494
|
+
if (!PLACEMENTS.has(placement))
|
|
1495
|
+
throw new TypeError(`Unknown placement: ${placement}`);
|
|
1247
1496
|
const [side, align = null] = placement.split("-");
|
|
1248
1497
|
return { side, align, crossAxis: crossAxisFor(side) };
|
|
1249
1498
|
}
|
|
1250
|
-
function flipSide(side) {
|
|
1251
|
-
return { top: "bottom", bottom: "top", left: "right", right: "left" }[side] || side;
|
|
1252
|
-
}
|
|
1253
1499
|
function computeCoords(reference, floating, side, align, rtl, distance) {
|
|
1254
1500
|
const crossAxis = crossAxisFor(side);
|
|
1255
1501
|
const commonX = reference.x + reference.width / 2 - floating.width / 2;
|
|
@@ -1281,17 +1527,12 @@
|
|
|
1281
1527
|
function overflowOn(position, size, start, end) {
|
|
1282
1528
|
return Math.max(start - position, 0) + Math.max(position + size - end, 0);
|
|
1283
1529
|
}
|
|
1284
|
-
function isRTL(
|
|
1285
|
-
const
|
|
1286
|
-
if (
|
|
1287
|
-
return
|
|
1288
|
-
if (direction === "ltr")
|
|
1289
|
-
return false;
|
|
1290
|
-
const win = element.ownerDocument?.defaultView;
|
|
1291
|
-
if (win?.CSS?.supports?.("selector(:dir(rtl))") && typeof element.matches === "function") {
|
|
1292
|
-
return element.matches(":dir(rtl)");
|
|
1530
|
+
function isRTL(reference) {
|
|
1531
|
+
const win = reference.ownerDocument?.defaultView;
|
|
1532
|
+
if (win && reference instanceof win.Element) {
|
|
1533
|
+
return win.getComputedStyle(reference).direction === "rtl";
|
|
1293
1534
|
}
|
|
1294
|
-
return
|
|
1535
|
+
return "dir" in reference && reference.dir === "rtl";
|
|
1295
1536
|
}
|
|
1296
1537
|
var STABLE_SCROLLBAR_MAX_WIDTH = 25;
|
|
1297
1538
|
function getViewportBoundary(doc) {
|
|
@@ -1329,8 +1570,14 @@
|
|
|
1329
1570
|
const percent = (referenceCenter - boxStart) / size * 100;
|
|
1330
1571
|
return Math.round(Math.min(100, Math.max(0, percent)) * 1000) / 1000;
|
|
1331
1572
|
}
|
|
1332
|
-
function isOutsideBoundary(
|
|
1333
|
-
|
|
1573
|
+
function isOutsideBoundary(rects, boundary) {
|
|
1574
|
+
for (let i = 0;i < rects.length; i++) {
|
|
1575
|
+
const rect = rects[i];
|
|
1576
|
+
if (rect.right >= boundary.x && rect.left <= boundary.right && rect.bottom >= boundary.y && rect.top <= boundary.bottom) {
|
|
1577
|
+
return false;
|
|
1578
|
+
}
|
|
1579
|
+
}
|
|
1580
|
+
return true;
|
|
1334
1581
|
}
|
|
1335
1582
|
function getAvailableHeight(referenceRect, side, boundary, distance, padding) {
|
|
1336
1583
|
if (side === "top") {
|
|
@@ -1385,10 +1632,22 @@
|
|
|
1385
1632
|
for (const [subscription, type] of notifications) {
|
|
1386
1633
|
if (!subscriptions.has(subscription) || !subscription.floating.isConnected)
|
|
1387
1634
|
continue;
|
|
1388
|
-
|
|
1635
|
+
try {
|
|
1636
|
+
subscription.callback({ type });
|
|
1637
|
+
} catch (error) {
|
|
1638
|
+
reportError(error);
|
|
1639
|
+
}
|
|
1389
1640
|
}
|
|
1390
1641
|
});
|
|
1391
1642
|
}
|
|
1643
|
+
function reportError(error) {
|
|
1644
|
+
if (typeof win.reportError === "function")
|
|
1645
|
+
win.reportError(error);
|
|
1646
|
+
else
|
|
1647
|
+
win.setTimeout(() => {
|
|
1648
|
+
throw error;
|
|
1649
|
+
});
|
|
1650
|
+
}
|
|
1392
1651
|
function notifyAll(type) {
|
|
1393
1652
|
for (const subscription of subscriptions)
|
|
1394
1653
|
queue(subscription, type);
|
|
@@ -1479,28 +1738,32 @@
|
|
|
1479
1738
|
return trackerFor(floating).add(reference, floating, callback);
|
|
1480
1739
|
}
|
|
1481
1740
|
function positionOnce(reference, floating, options) {
|
|
1741
|
+
const placement = options.placement === undefined ? "bottom-start" : options.placement;
|
|
1742
|
+
let { side, align, crossAxis } = parsePlacement(placement);
|
|
1743
|
+
const coordinateSpace = options.coordinateSpace === undefined ? "viewport" : options.coordinateSpace;
|
|
1744
|
+
if (!COORDINATE_SPACES.has(coordinateSpace)) {
|
|
1745
|
+
throw new TypeError(`Unknown coordinateSpace: ${coordinateSpace}`);
|
|
1746
|
+
}
|
|
1482
1747
|
if (!isVisible(floating))
|
|
1483
1748
|
return null;
|
|
1484
|
-
const placement = options.placement || "bottom-start";
|
|
1485
1749
|
const distance = options.distance || 0;
|
|
1486
1750
|
const flip = options.flip !== false;
|
|
1487
1751
|
const shift = options.shift !== false;
|
|
1488
1752
|
const shiftPadding = options.shiftPadding ?? 4;
|
|
1489
|
-
let { side, align, crossAxis } = parsePlacement(placement);
|
|
1490
1753
|
const rtl = align ? isRTL(reference) : false;
|
|
1491
1754
|
const rects = reference.getClientRects();
|
|
1492
|
-
|
|
1493
|
-
if (!referenceRect)
|
|
1755
|
+
if (!rects.length)
|
|
1494
1756
|
return null;
|
|
1757
|
+
const rectFor = (s) => s === "bottom" ? rects[rects.length - 1] : rects[0];
|
|
1495
1758
|
const boundary = getBoundary(reference, options);
|
|
1496
|
-
if (!boundary || isOutsideBoundary(
|
|
1759
|
+
if (!boundary || isOutsideBoundary(rects, boundary))
|
|
1497
1760
|
return null;
|
|
1498
1761
|
const floatingRect = getFloatingSize(floating);
|
|
1499
1762
|
const limits = {
|
|
1500
1763
|
x: { size: floatingRect.width, start: boundary.x, end: boundary.right },
|
|
1501
1764
|
y: { size: floatingRect.height, start: boundary.y, end: boundary.bottom }
|
|
1502
1765
|
};
|
|
1503
|
-
const place = (nextSide, nextAlign) => computeCoords(
|
|
1766
|
+
const place = (nextSide, nextAlign) => computeCoords(rectFor(nextSide), floatingRect, nextSide, nextAlign, rtl, distance);
|
|
1504
1767
|
const overflowAt = (position, axis) => {
|
|
1505
1768
|
const { size, start, end } = limits[axis];
|
|
1506
1769
|
return overflowOn(position, size, start + shiftPadding, end - shiftPadding);
|
|
@@ -1510,31 +1773,30 @@
|
|
|
1510
1773
|
return overflowAt(shift ? clampToBoundary(position, size, start, end, shiftPadding) : position, axis);
|
|
1511
1774
|
};
|
|
1512
1775
|
let coords = place(side, align);
|
|
1776
|
+
const overflowFor = (nextSide, at) => {
|
|
1777
|
+
const cross = crossAxisFor(nextSide);
|
|
1778
|
+
const main = cross === "x" ? "y" : "x";
|
|
1779
|
+
const mainOverflow = overflowAt(at[main], main);
|
|
1780
|
+
return { main: mainOverflow, total: mainOverflow + shiftedOverflowAt(at[cross], cross) };
|
|
1781
|
+
};
|
|
1513
1782
|
if (flip) {
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
const
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
coords = flipped;
|
|
1523
|
-
overflow = flippedOverflow;
|
|
1524
|
-
}
|
|
1525
|
-
}
|
|
1526
|
-
if (mainAxis === "x" && overflow > 0) {
|
|
1527
|
-
const above = place("top", align);
|
|
1528
|
-
const below = place("bottom", align);
|
|
1529
|
-
const useBottom = overflowAt(below.y, "y") < overflowAt(above.y, "y");
|
|
1530
|
-
const candidate = useBottom ? below : above;
|
|
1531
|
-
const swapped = overflowAt(candidate.y, "y") + shiftedOverflowAt(candidate.x, "x");
|
|
1532
|
-
if (swapped < overflow + shiftedOverflowAt(coords.y, "y")) {
|
|
1533
|
-
side = useBottom ? "bottom" : "top";
|
|
1534
|
-
crossAxis = "x";
|
|
1535
|
-
coords = candidate;
|
|
1783
|
+
let best = overflowFor(side, coords);
|
|
1784
|
+
const consider = (nextSide) => {
|
|
1785
|
+
const next = place(nextSide, align);
|
|
1786
|
+
const overflow = overflowFor(nextSide, next);
|
|
1787
|
+
if (overflow.total < best.total) {
|
|
1788
|
+
side = nextSide;
|
|
1789
|
+
coords = next;
|
|
1790
|
+
best = overflow;
|
|
1536
1791
|
}
|
|
1792
|
+
};
|
|
1793
|
+
if (best.main > 0)
|
|
1794
|
+
consider(OPPOSITE[side]);
|
|
1795
|
+
if (crossAxis === "y" && best.main > 0) {
|
|
1796
|
+
consider("top");
|
|
1797
|
+
consider("bottom");
|
|
1537
1798
|
}
|
|
1799
|
+
crossAxis = crossAxisFor(side);
|
|
1538
1800
|
}
|
|
1539
1801
|
if (crossAxis === "x" && shift && align) {
|
|
1540
1802
|
const overflow = overflowAt(coords.x, "x");
|
|
@@ -1551,12 +1813,13 @@
|
|
|
1551
1813
|
const { size, start, end } = limits[crossAxis];
|
|
1552
1814
|
coords[crossAxis] = clampToBoundary(coords[crossAxis], size, start, end, shiftPadding);
|
|
1553
1815
|
}
|
|
1554
|
-
const
|
|
1555
|
-
const
|
|
1556
|
-
const
|
|
1816
|
+
const resolvedRect = rectFor(side);
|
|
1817
|
+
const arrowX = arrowPercent(resolvedRect.x + resolvedRect.width / 2, coords.x, floatingRect.width);
|
|
1818
|
+
const arrowY = arrowPercent(resolvedRect.y + resolvedRect.height / 2, coords.y, floatingRect.height);
|
|
1819
|
+
const availableHeight = `${getAvailableHeight(resolvedRect, side, boundary, distance, shiftPadding)}px`;
|
|
1557
1820
|
const { style } = floating;
|
|
1558
1821
|
const roomChanged = style.getPropertyValue("--available-height") !== availableHeight;
|
|
1559
|
-
const win =
|
|
1822
|
+
const win = coordinateSpace === "document" ? reference.ownerDocument.defaultView : null;
|
|
1560
1823
|
const originX = win ? win.scrollX : 0;
|
|
1561
1824
|
const originY = win ? win.scrollY : 0;
|
|
1562
1825
|
style.left = `${coords.x + originX}px`;
|
|
@@ -1684,9 +1947,9 @@
|
|
|
1684
1947
|
input.setCustomValidity("");
|
|
1685
1948
|
return;
|
|
1686
1949
|
}
|
|
1687
|
-
input.setCustomValidity(
|
|
1950
|
+
input.setCustomValidity(this.messages.invalidDate);
|
|
1688
1951
|
}
|
|
1689
|
-
async commit() {
|
|
1952
|
+
async commit(options = {}) {
|
|
1690
1953
|
const input = this.input;
|
|
1691
1954
|
const commitId = ++this._commitId;
|
|
1692
1955
|
const text = input.value.trim();
|
|
@@ -1706,7 +1969,7 @@
|
|
|
1706
1969
|
return { status: "invalid" };
|
|
1707
1970
|
}
|
|
1708
1971
|
if (this.confirm) {
|
|
1709
|
-
const result = await this.confirm(parsed);
|
|
1972
|
+
const result = await this.confirm(parsed, options);
|
|
1710
1973
|
if (commitId !== this._commitId)
|
|
1711
1974
|
return { status: "stale" };
|
|
1712
1975
|
if (!result?.ok) {
|
|
@@ -1778,6 +2041,7 @@
|
|
|
1778
2041
|
|
|
1779
2042
|
// src/date-picker.js
|
|
1780
2043
|
var uid2 = 0;
|
|
2044
|
+
var DRAG_THRESHOLD = 4;
|
|
1781
2045
|
|
|
1782
2046
|
class DatePickerElement extends HTMLElement {
|
|
1783
2047
|
static observedAttributes = ["value", "locale", "min", "max", "open-on-focus", "month-format"];
|
|
@@ -1797,6 +2061,9 @@
|
|
|
1797
2061
|
this._lastFocusEndpoint = "";
|
|
1798
2062
|
this._rangeCommitId = 0;
|
|
1799
2063
|
this._pendingIntents = new Map;
|
|
2064
|
+
this._previewDate = "";
|
|
2065
|
+
this._drag = null;
|
|
2066
|
+
this._suppressGridClick = false;
|
|
1800
2067
|
this._buttons = [];
|
|
1801
2068
|
this._returnFocus = null;
|
|
1802
2069
|
this._panel = null;
|
|
@@ -1810,6 +2077,7 @@
|
|
|
1810
2077
|
this._open = false;
|
|
1811
2078
|
this._suppressFocusOpen = false;
|
|
1812
2079
|
this._messages = getDefaultMessages();
|
|
2080
|
+
this._controlClasses = getDefaultControlClasses();
|
|
1813
2081
|
this._source = null;
|
|
1814
2082
|
this._dateState = null;
|
|
1815
2083
|
this._renderDay = null;
|
|
@@ -1839,7 +2107,7 @@
|
|
|
1839
2107
|
this._discoverTimeFields();
|
|
1840
2108
|
this._field = new DateFieldController(input, { messages: this._messages, locale: this.locale });
|
|
1841
2109
|
this._field.onAttributesChanged = () => this._syncInputState();
|
|
1842
|
-
this._field.confirm = (date) => this._confirmDate(date);
|
|
2110
|
+
this._field.confirm = (date, options) => this._confirmDate(date, options);
|
|
1843
2111
|
this._setupFormValue();
|
|
1844
2112
|
this._build();
|
|
1845
2113
|
this._syncInputState();
|
|
@@ -1903,7 +2171,7 @@
|
|
|
1903
2171
|
];
|
|
1904
2172
|
for (const field of this._fields) {
|
|
1905
2173
|
field.onAttributesChanged = () => this._syncRangeFields();
|
|
1906
|
-
field.confirm = (date) => this._confirmDate(date);
|
|
2174
|
+
field.confirm = (date, options) => this._confirmDate(date, options);
|
|
1907
2175
|
field.setupFormValue();
|
|
1908
2176
|
}
|
|
1909
2177
|
this._build();
|
|
@@ -2064,6 +2332,17 @@
|
|
|
2064
2332
|
get open() {
|
|
2065
2333
|
return this._open;
|
|
2066
2334
|
}
|
|
2335
|
+
get controlClasses() {
|
|
2336
|
+
return { ...this._controlClasses };
|
|
2337
|
+
}
|
|
2338
|
+
set controlClasses(value) {
|
|
2339
|
+
const classes = resolveControlClasses(value);
|
|
2340
|
+
if (sameControlClasses(classes, this._controlClasses))
|
|
2341
|
+
return;
|
|
2342
|
+
this._controlClasses = classes;
|
|
2343
|
+
if (this._calendar)
|
|
2344
|
+
this._calendar.controlClasses = this._controlClasses;
|
|
2345
|
+
}
|
|
2067
2346
|
get messages() {
|
|
2068
2347
|
return this._messages;
|
|
2069
2348
|
}
|
|
@@ -2148,6 +2427,7 @@
|
|
|
2148
2427
|
const field = this._fields[index];
|
|
2149
2428
|
button.disabled = Boolean(field?.input.disabled || field?.input.readOnly);
|
|
2150
2429
|
}
|
|
2430
|
+
this._syncDragAffordance();
|
|
2151
2431
|
const active = this._range?.activeEndpoint === "end" ? 1 : 0;
|
|
2152
2432
|
if (this._open && (this._fields[active]?.input.disabled || this._fields[active]?.input.readOnly)) {
|
|
2153
2433
|
this.hide(false);
|
|
@@ -2226,6 +2506,23 @@
|
|
|
2226
2506
|
this._refreshLocale();
|
|
2227
2507
|
this._refreshButtonLabel();
|
|
2228
2508
|
}
|
|
2509
|
+
_bindPopup(signal, input, restore) {
|
|
2510
|
+
this.ownerDocument.addEventListener("pointerdown", (event) => {
|
|
2511
|
+
if (!this._open || event.composedPath().includes(this))
|
|
2512
|
+
return;
|
|
2513
|
+
this.hide(false);
|
|
2514
|
+
}, { capture: true, signal });
|
|
2515
|
+
this.ownerDocument.addEventListener("reset", (event) => {
|
|
2516
|
+
if (!(event.target instanceof HTMLFormElement) || !input || event.target !== input.form)
|
|
2517
|
+
return;
|
|
2518
|
+
this.hide(false);
|
|
2519
|
+
queueMicrotask(() => {
|
|
2520
|
+
if (!event.defaultPrevented)
|
|
2521
|
+
restore();
|
|
2522
|
+
});
|
|
2523
|
+
}, { capture: true, signal });
|
|
2524
|
+
this.addEventListener("keydown", (event) => this._onEscape(event), { signal });
|
|
2525
|
+
}
|
|
2229
2526
|
_bind() {
|
|
2230
2527
|
const input = this._input;
|
|
2231
2528
|
const button = this._buttons[0]?.button;
|
|
@@ -2288,22 +2585,7 @@
|
|
|
2288
2585
|
});
|
|
2289
2586
|
}, { signal });
|
|
2290
2587
|
calendar.addEventListener("dateloadend", () => void this.validate(), { signal });
|
|
2291
|
-
this.
|
|
2292
|
-
if (!this._open || event.composedPath().includes(this))
|
|
2293
|
-
return;
|
|
2294
|
-
this.hide(false);
|
|
2295
|
-
}, { capture: true, signal });
|
|
2296
|
-
this.ownerDocument.addEventListener("reset", (event) => {
|
|
2297
|
-
const form = event.target;
|
|
2298
|
-
if (form instanceof HTMLFormElement && this._input && form === this._input.form) {
|
|
2299
|
-
this.hide(false);
|
|
2300
|
-
queueMicrotask(() => {
|
|
2301
|
-
if (!event.defaultPrevented)
|
|
2302
|
-
this._restoreDefault();
|
|
2303
|
-
});
|
|
2304
|
-
}
|
|
2305
|
-
}, { capture: true, signal });
|
|
2306
|
-
this.addEventListener("keydown", (event) => this._onEscape(event), { signal });
|
|
2588
|
+
this._bindPopup(signal, this._input, () => this._restoreDefault());
|
|
2307
2589
|
}
|
|
2308
2590
|
_bindRange() {
|
|
2309
2591
|
const calendar = this._calendar;
|
|
@@ -2331,7 +2613,15 @@
|
|
|
2331
2613
|
signal
|
|
2332
2614
|
});
|
|
2333
2615
|
}
|
|
2334
|
-
calendar.addEventListener("click", (event) =>
|
|
2616
|
+
calendar.addEventListener("click", (event) => {
|
|
2617
|
+
if (this._suppressGridClick) {
|
|
2618
|
+
this._suppressGridClick = false;
|
|
2619
|
+
event.preventDefault();
|
|
2620
|
+
event.stopImmediatePropagation();
|
|
2621
|
+
return;
|
|
2622
|
+
}
|
|
2623
|
+
this._captureGridIntent(event);
|
|
2624
|
+
}, { capture: true, signal });
|
|
2335
2625
|
calendar.addEventListener("keydown", (event) => this._captureGridIntent(event), {
|
|
2336
2626
|
capture: true,
|
|
2337
2627
|
signal
|
|
@@ -2344,15 +2634,12 @@
|
|
|
2344
2634
|
queueMicrotask(() => {
|
|
2345
2635
|
if (custom.defaultPrevented || !this._connected || !this._range)
|
|
2346
2636
|
return;
|
|
2347
|
-
const
|
|
2348
|
-
if (
|
|
2637
|
+
const intent = this._pendingIntents.get(date);
|
|
2638
|
+
if (intent === undefined || intent.id !== this._rangeCommitId)
|
|
2349
2639
|
return;
|
|
2350
2640
|
this._pendingIntents.delete(date);
|
|
2351
|
-
const
|
|
2352
|
-
|
|
2353
|
-
const field = this._fields?.[index];
|
|
2354
|
-
return field != null && !field.input.disabled && !field.input.readOnly;
|
|
2355
|
-
});
|
|
2641
|
+
const editable = (bound) => this._boundEditable(bound);
|
|
2642
|
+
const result = intent.endpoint ? this._range.moveEndpoint(date, intent.endpoint, editable) : this._range.activate(date, editable);
|
|
2356
2643
|
if (result.status === "refused") {
|
|
2357
2644
|
this._calendar?.dispatchEvent(new CustomEvent("dateinvalid", {
|
|
2358
2645
|
detail: { date, state: this._calendar.getDateState(date) },
|
|
@@ -2360,30 +2647,25 @@
|
|
|
2360
2647
|
}));
|
|
2361
2648
|
return;
|
|
2362
2649
|
}
|
|
2363
|
-
this.
|
|
2650
|
+
this._applyRangeTransition(result);
|
|
2651
|
+
if (intent.endpoint)
|
|
2652
|
+
return;
|
|
2364
2653
|
if (result.close || result.status === "complete") {
|
|
2365
2654
|
this.hide(false);
|
|
2366
2655
|
this._focusField(result.endpoint);
|
|
2367
2656
|
}
|
|
2368
2657
|
});
|
|
2369
2658
|
}, { signal });
|
|
2659
|
+
calendar.addEventListener("datefocus", (event) => this._onGridDateFocus(event), { signal });
|
|
2660
|
+
calendar.addEventListener("pointerover", (event) => this._onGridPointerOver(event), { signal });
|
|
2661
|
+
calendar.addEventListener("pointerleave", () => this._onGridPointerLeave(), { signal });
|
|
2662
|
+
calendar.addEventListener("pointerdown", (event) => this._onGridPointerDown(event), { signal });
|
|
2663
|
+
calendar.addEventListener("pointermove", (event) => this._onGridPointerMove(event), { signal });
|
|
2664
|
+
calendar.addEventListener("pointerup", (event) => this._onGridPointerUp(event), { signal });
|
|
2665
|
+
calendar.addEventListener("pointercancel", (event) => this._onGridPointerCancel(event), { signal });
|
|
2666
|
+
calendar.addEventListener("lostpointercapture", (event) => this._onGridPointerCancel(event), { signal });
|
|
2370
2667
|
calendar.addEventListener("dateloadend", () => void this.validate(), { signal });
|
|
2371
|
-
this.
|
|
2372
|
-
if (!this._open || event.composedPath().includes(this))
|
|
2373
|
-
return;
|
|
2374
|
-
this.hide(false);
|
|
2375
|
-
}, { capture: true, signal });
|
|
2376
|
-
this.ownerDocument.addEventListener("reset", (event) => {
|
|
2377
|
-
const form = event.target;
|
|
2378
|
-
if (form instanceof HTMLFormElement && this._fields && form === this._fields[0].input.form) {
|
|
2379
|
-
this.hide(false);
|
|
2380
|
-
queueMicrotask(() => {
|
|
2381
|
-
if (!event.defaultPrevented)
|
|
2382
|
-
this._restoreRangeDefault();
|
|
2383
|
-
});
|
|
2384
|
-
}
|
|
2385
|
-
}, { capture: true, signal });
|
|
2386
|
-
this.addEventListener("keydown", (event) => this._onEscape(event), { signal });
|
|
2668
|
+
this._bindPopup(signal, this._fields?.[0]?.input, () => this._restoreRangeDefault());
|
|
2387
2669
|
}
|
|
2388
2670
|
_activateRangeEndpoint(endpoint) {
|
|
2389
2671
|
const calendar = this._calendar;
|
|
@@ -2396,6 +2678,7 @@
|
|
|
2396
2678
|
const target = this._boundCanonical(endpoint) || todayISO();
|
|
2397
2679
|
calendar.display = monthKey(target);
|
|
2398
2680
|
calendar.focusedDate = target;
|
|
2681
|
+
this._clearPreview();
|
|
2399
2682
|
}
|
|
2400
2683
|
_onRangeTriggerClick(endpoint, event) {
|
|
2401
2684
|
if (!this._open) {
|
|
@@ -2478,7 +2761,190 @@
|
|
|
2478
2761
|
if (!isDate(date))
|
|
2479
2762
|
return;
|
|
2480
2763
|
this._pendingIntents.clear();
|
|
2481
|
-
this._pendingIntents.set(date, ++this._rangeCommitId);
|
|
2764
|
+
this._pendingIntents.set(date, { id: ++this._rangeCommitId, endpoint: "" });
|
|
2765
|
+
}
|
|
2766
|
+
_boundEditable(bound) {
|
|
2767
|
+
const field = this._fields?.[bound === "end" ? 1 : 0];
|
|
2768
|
+
return field != null && !field.input.disabled && !field.input.readOnly;
|
|
2769
|
+
}
|
|
2770
|
+
_applyRangeTransition(transition) {
|
|
2771
|
+
const fields = this._fields;
|
|
2772
|
+
if (!fields || !this._range || transition.status === "refused")
|
|
2773
|
+
return;
|
|
2774
|
+
const moved = [];
|
|
2775
|
+
for (const which of transition.changedEndpoints) {
|
|
2776
|
+
const field = fields[which === "end" ? 1 : 0];
|
|
2777
|
+
const next = which === "end" ? transition.range.end : transition.range.start;
|
|
2778
|
+
if (!field || field.canonical === next)
|
|
2779
|
+
continue;
|
|
2780
|
+
field.setCanonical(next, { format: true });
|
|
2781
|
+
moved.push(field);
|
|
2782
|
+
}
|
|
2783
|
+
this._syncHighlight();
|
|
2784
|
+
this._refreshRangeButtonLabel();
|
|
2785
|
+
this._revalidateRange(transition.endpoint);
|
|
2786
|
+
if (!moved.length)
|
|
2787
|
+
return;
|
|
2788
|
+
this._emitRangeChange();
|
|
2789
|
+
for (const field of moved) {
|
|
2790
|
+
field.input.dispatchEvent(new Event("input", { bubbles: true }));
|
|
2791
|
+
field.input.dispatchEvent(new Event("change", { bubbles: true }));
|
|
2792
|
+
}
|
|
2793
|
+
}
|
|
2794
|
+
_setPreview(date, endpoint = "") {
|
|
2795
|
+
const calendar = this._calendar;
|
|
2796
|
+
if (!calendar || !this._range || !this._open)
|
|
2797
|
+
return;
|
|
2798
|
+
const candidate = isDate(date) && !calendar.getDateState(date).disabled ? date : "";
|
|
2799
|
+
const preview = candidate ? this._range.previewRange(candidate, endpoint, (bound) => this._boundEditable(bound)) : null;
|
|
2800
|
+
if (!preview) {
|
|
2801
|
+
this._clearPreview();
|
|
2802
|
+
return;
|
|
2803
|
+
}
|
|
2804
|
+
this._previewDate = candidate;
|
|
2805
|
+
calendar.toggleAttribute("data-range-preview", true);
|
|
2806
|
+
calendar.highlightedRange = preview;
|
|
2807
|
+
}
|
|
2808
|
+
_clearPreview() {
|
|
2809
|
+
if (!this._previewDate)
|
|
2810
|
+
return;
|
|
2811
|
+
this._syncHighlight();
|
|
2812
|
+
}
|
|
2813
|
+
_onGridDateFocus(event) {
|
|
2814
|
+
if (this._drag)
|
|
2815
|
+
return;
|
|
2816
|
+
const date = event.detail?.date;
|
|
2817
|
+
if (isDate(date))
|
|
2818
|
+
this._setPreview(date);
|
|
2819
|
+
}
|
|
2820
|
+
_onGridPointerOver(event) {
|
|
2821
|
+
const pointer = event;
|
|
2822
|
+
if (this._drag || pointer.pointerType === "touch")
|
|
2823
|
+
return;
|
|
2824
|
+
this._setPreview(this._cellDate(pointer.target));
|
|
2825
|
+
}
|
|
2826
|
+
_onGridPointerLeave() {
|
|
2827
|
+
if (this._drag?.active === false)
|
|
2828
|
+
this._drag = null;
|
|
2829
|
+
if (this._drag)
|
|
2830
|
+
return;
|
|
2831
|
+
this._clearPreview();
|
|
2832
|
+
}
|
|
2833
|
+
_cellDate(target) {
|
|
2834
|
+
const cell = target instanceof Element ? target.closest(".dp-day[data-date]") : null;
|
|
2835
|
+
const date = cell?.getAttribute("data-date") || "";
|
|
2836
|
+
return isDate(date) ? date : "";
|
|
2837
|
+
}
|
|
2838
|
+
_dateAtPoint(x, y) {
|
|
2839
|
+
const element = this.ownerDocument.elementFromPoint(x, y);
|
|
2840
|
+
if (!(element instanceof Element) || !this._calendar?.contains(element))
|
|
2841
|
+
return "";
|
|
2842
|
+
return this._cellDate(element);
|
|
2843
|
+
}
|
|
2844
|
+
_onGridPointerDown(event) {
|
|
2845
|
+
const pointer = event;
|
|
2846
|
+
this._suppressGridClick = false;
|
|
2847
|
+
if (this._drag?.active === false)
|
|
2848
|
+
this._drag = null;
|
|
2849
|
+
if (!this._open || !this._range || this._drag)
|
|
2850
|
+
return;
|
|
2851
|
+
if (!pointer.isPrimary || pointer.button !== 0)
|
|
2852
|
+
return;
|
|
2853
|
+
if (pointer.pointerType !== "mouse" && pointer.pointerType !== "pen")
|
|
2854
|
+
return;
|
|
2855
|
+
if (!this._range.complete)
|
|
2856
|
+
return;
|
|
2857
|
+
const cell = pointer.target instanceof Element ? pointer.target.closest(".dp-day[data-date]") : null;
|
|
2858
|
+
if (!(cell instanceof HTMLElement))
|
|
2859
|
+
return;
|
|
2860
|
+
const isStart = cell.hasAttribute("data-range-start");
|
|
2861
|
+
const isEnd = cell.hasAttribute("data-range-end");
|
|
2862
|
+
if (isStart === isEnd)
|
|
2863
|
+
return;
|
|
2864
|
+
const endpoint = isStart ? "start" : "end";
|
|
2865
|
+
const date = this._cellDate(cell);
|
|
2866
|
+
if (!date || !this._boundEditable(endpoint))
|
|
2867
|
+
return;
|
|
2868
|
+
this._drag = {
|
|
2869
|
+
pointerId: pointer.pointerId,
|
|
2870
|
+
endpoint,
|
|
2871
|
+
x: pointer.clientX,
|
|
2872
|
+
y: pointer.clientY,
|
|
2873
|
+
date,
|
|
2874
|
+
active: false
|
|
2875
|
+
};
|
|
2876
|
+
}
|
|
2877
|
+
_onGridPointerMove(event) {
|
|
2878
|
+
const pointer = event;
|
|
2879
|
+
const drag = this._drag;
|
|
2880
|
+
if (!drag || pointer.pointerId !== drag.pointerId)
|
|
2881
|
+
return;
|
|
2882
|
+
if (!drag.active) {
|
|
2883
|
+
const travelled = Math.abs(pointer.clientX - drag.x) > DRAG_THRESHOLD || Math.abs(pointer.clientY - drag.y) > DRAG_THRESHOLD;
|
|
2884
|
+
if (!travelled)
|
|
2885
|
+
return;
|
|
2886
|
+
drag.active = true;
|
|
2887
|
+
try {
|
|
2888
|
+
this._calendar?.setPointerCapture(drag.pointerId);
|
|
2889
|
+
} catch {}
|
|
2890
|
+
this._syncDragAffordance();
|
|
2891
|
+
}
|
|
2892
|
+
const date = this._dateAtPoint(pointer.clientX, pointer.clientY);
|
|
2893
|
+
if (!date || this._calendar?.getDateState(date).disabled)
|
|
2894
|
+
return;
|
|
2895
|
+
drag.date = date;
|
|
2896
|
+
this._setPreview(date, drag.endpoint);
|
|
2897
|
+
}
|
|
2898
|
+
_onGridPointerUp(event) {
|
|
2899
|
+
const pointer = event;
|
|
2900
|
+
const drag = this._drag;
|
|
2901
|
+
if (!drag || pointer.pointerId !== drag.pointerId)
|
|
2902
|
+
return;
|
|
2903
|
+
this._drag = null;
|
|
2904
|
+
this._releaseDragCapture(drag.pointerId);
|
|
2905
|
+
if (!drag.active) {
|
|
2906
|
+
this._syncDragAffordance();
|
|
2907
|
+
return;
|
|
2908
|
+
}
|
|
2909
|
+
this._suppressGridClick = true;
|
|
2910
|
+
const transition = this._range?.projectEndpoint(drag.date, drag.endpoint, (bound) => this._boundEditable(bound));
|
|
2911
|
+
if (!transition || transition.status === "refused" || !transition.changedEndpoints.length) {
|
|
2912
|
+
this._syncHighlight();
|
|
2913
|
+
return;
|
|
2914
|
+
}
|
|
2915
|
+
const target = drag.endpoint === "start" ? transition.range.start : transition.range.end;
|
|
2916
|
+
this._pendingIntents.clear();
|
|
2917
|
+
this._pendingIntents.set(target, { id: ++this._rangeCommitId, endpoint: drag.endpoint });
|
|
2918
|
+
this._calendar?.activateDate(target);
|
|
2919
|
+
}
|
|
2920
|
+
_onGridPointerCancel(event) {
|
|
2921
|
+
const pointer = event;
|
|
2922
|
+
const drag = this._drag;
|
|
2923
|
+
if (!drag || pointer.pointerId !== drag.pointerId)
|
|
2924
|
+
return;
|
|
2925
|
+
this._drag = null;
|
|
2926
|
+
this._releaseDragCapture(drag.pointerId);
|
|
2927
|
+
this._syncHighlight();
|
|
2928
|
+
}
|
|
2929
|
+
_releaseDragCapture(pointerId) {
|
|
2930
|
+
const calendar = this._calendar;
|
|
2931
|
+
if (calendar?.hasPointerCapture(pointerId))
|
|
2932
|
+
calendar.releasePointerCapture(pointerId);
|
|
2933
|
+
}
|
|
2934
|
+
_syncDragAffordance() {
|
|
2935
|
+
const calendar = this._calendar;
|
|
2936
|
+
if (!calendar || !this._range)
|
|
2937
|
+
return;
|
|
2938
|
+
if (this._drag?.active) {
|
|
2939
|
+
calendar.setAttribute("data-range-drag", "active");
|
|
2940
|
+
return;
|
|
2941
|
+
}
|
|
2942
|
+
const { start, end } = this._range;
|
|
2943
|
+
const draggable = this._range.complete && start !== end && (this._boundEditable("start") || this._boundEditable("end"));
|
|
2944
|
+
if (draggable)
|
|
2945
|
+
calendar.setAttribute("data-range-drag", "ready");
|
|
2946
|
+
else
|
|
2947
|
+
calendar.removeAttribute("data-range-drag");
|
|
2482
2948
|
}
|
|
2483
2949
|
_onEscape(event) {
|
|
2484
2950
|
if (event.key === "Escape" && this._open) {
|
|
@@ -2496,6 +2962,7 @@
|
|
|
2496
2962
|
calendar.min = this.min;
|
|
2497
2963
|
calendar.max = this.max;
|
|
2498
2964
|
calendar.monthFormat = this.monthFormat;
|
|
2965
|
+
calendar.controlClasses = this._controlClasses;
|
|
2499
2966
|
calendar.messages = this._messages;
|
|
2500
2967
|
calendar.source = this._source;
|
|
2501
2968
|
calendar.dateState = this._dateState;
|
|
@@ -2581,7 +3048,7 @@
|
|
|
2581
3048
|
this._field?.setCanonical(this._value, { format: options.format !== false });
|
|
2582
3049
|
if (this._calendar) {
|
|
2583
3050
|
this._calendar.value = this._value;
|
|
2584
|
-
if (this._value)
|
|
3051
|
+
if (this._value && options.moveDisplay !== false)
|
|
2585
3052
|
this._calendar.focusedDate = this._value;
|
|
2586
3053
|
}
|
|
2587
3054
|
this._refreshButtonLabel();
|
|
@@ -2594,13 +3061,15 @@
|
|
|
2594
3061
|
}
|
|
2595
3062
|
}
|
|
2596
3063
|
}
|
|
2597
|
-
async _confirmDate(date) {
|
|
3064
|
+
async _confirmDate(date, options = {}) {
|
|
2598
3065
|
const calendar = this._calendar;
|
|
2599
3066
|
if (!calendar)
|
|
2600
3067
|
return { ok: true };
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
calendar.display
|
|
3068
|
+
if (options.moveDisplay !== false) {
|
|
3069
|
+
const display = monthKey(date);
|
|
3070
|
+
if (calendar.display !== display)
|
|
3071
|
+
calendar.display = display;
|
|
3072
|
+
}
|
|
2604
3073
|
const confirmed = await calendar.ensureDate(date);
|
|
2605
3074
|
const state = calendar.getDateState(date);
|
|
2606
3075
|
if (!confirmed || state.disabled)
|
|
@@ -2613,10 +3082,14 @@
|
|
|
2613
3082
|
return false;
|
|
2614
3083
|
if (!options.force && !field.isDirty)
|
|
2615
3084
|
return false;
|
|
2616
|
-
const result = await field.commit();
|
|
3085
|
+
const result = await field.commit(options);
|
|
2617
3086
|
if (result.status === "stale" || result.status === "invalid")
|
|
2618
3087
|
return false;
|
|
2619
|
-
this._setValue(result.value || "", {
|
|
3088
|
+
this._setValue(result.value || "", {
|
|
3089
|
+
emit,
|
|
3090
|
+
format: result.status === "ok",
|
|
3091
|
+
moveDisplay: options.moveDisplay
|
|
3092
|
+
});
|
|
2620
3093
|
return true;
|
|
2621
3094
|
}
|
|
2622
3095
|
async _commitFieldText(which) {
|
|
@@ -2691,19 +3164,21 @@
|
|
|
2691
3164
|
return;
|
|
2692
3165
|
this.dispatchEvent(new CustomEvent("rangechange", { detail: { ...this._range.range }, bubbles: true }));
|
|
2693
3166
|
}
|
|
3167
|
+
_displayRange() {
|
|
3168
|
+
const { start, end } = this._range?.range ?? { start: "", end: "" };
|
|
3169
|
+
if (!start)
|
|
3170
|
+
return { start: "", end: "" };
|
|
3171
|
+
if (end && compareDates(end, start) < 0)
|
|
3172
|
+
return { start, end: "" };
|
|
3173
|
+
return { start, end };
|
|
3174
|
+
}
|
|
2694
3175
|
_syncHighlight() {
|
|
2695
3176
|
if (!this._calendar || !this._range)
|
|
2696
3177
|
return;
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
}
|
|
2702
|
-
if (end && compareDates(end, start) < 0) {
|
|
2703
|
-
this._calendar.highlightedRange = { start, end: "" };
|
|
2704
|
-
} else {
|
|
2705
|
-
this._calendar.highlightedRange = { start, end };
|
|
2706
|
-
}
|
|
3178
|
+
this._previewDate = "";
|
|
3179
|
+
this._calendar.removeAttribute("data-range-preview");
|
|
3180
|
+
this._calendar.highlightedRange = this._displayRange();
|
|
3181
|
+
this._syncDragAffordance();
|
|
2707
3182
|
}
|
|
2708
3183
|
_revalidateRange(which) {
|
|
2709
3184
|
if (!this._fields || !this._range)
|
|
@@ -2825,7 +3300,7 @@
|
|
|
2825
3300
|
if (!fields)
|
|
2826
3301
|
return true;
|
|
2827
3302
|
for (const [index, field] of fields.entries()) {
|
|
2828
|
-
const result = await field.commit();
|
|
3303
|
+
const result = await field.commit({ moveDisplay: false });
|
|
2829
3304
|
if (result.status === "ok" || result.status === "clear") {
|
|
2830
3305
|
const which = index === 1 ? "end" : "start";
|
|
2831
3306
|
this._setBound(which, result.value || "", { format: result.status === "ok" });
|
|
@@ -2841,7 +3316,7 @@
|
|
|
2841
3316
|
if (!input.value.trim()) {
|
|
2842
3317
|
input.setCustomValidity("");
|
|
2843
3318
|
} else {
|
|
2844
|
-
await this._commitText(false, { force: true });
|
|
3319
|
+
await this._commitText(false, { force: true, moveDisplay: false });
|
|
2845
3320
|
}
|
|
2846
3321
|
this._revalidateTimeOrder("start");
|
|
2847
3322
|
this._revalidateTimeOrder("end");
|
|
@@ -2904,6 +3379,12 @@
|
|
|
2904
3379
|
this._setExpanded(false);
|
|
2905
3380
|
this._rangeCommitId++;
|
|
2906
3381
|
this._pendingIntents.clear();
|
|
3382
|
+
this._suppressGridClick = false;
|
|
3383
|
+
if (this._drag) {
|
|
3384
|
+
this._releaseDragCapture(this._drag.pointerId);
|
|
3385
|
+
this._drag = null;
|
|
3386
|
+
}
|
|
3387
|
+
this._syncHighlight();
|
|
2907
3388
|
if (restoreFocus) {
|
|
2908
3389
|
if (this._rangeMode()) {
|
|
2909
3390
|
const endpoint = this._restoreFocusEndpoint();
|