@rolldate/mcp 1.0.0 → 1.1.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/bin/rolldate-mcp.js +2 -2
- package/package.json +2 -2
- package/src/catalog.js +70 -0
- package/vendor/rolldate/rolldate.css +97 -17
- package/vendor/rolldate/rolldate.js +989 -551
- package/vendor/rolldate/rolldate.min.css +1 -1
- package/vendor/rolldate/rolldate.min.js +1 -1
|
@@ -6,9 +6,6 @@
|
|
|
6
6
|
var RollDate = (function () {
|
|
7
7
|
'use strict';
|
|
8
8
|
|
|
9
|
-
function getTranslation(text) {
|
|
10
|
-
return text
|
|
11
|
-
}
|
|
12
9
|
function getDecade(year) {
|
|
13
10
|
return Math.floor(year / 10) * 10
|
|
14
11
|
}
|
|
@@ -453,6 +450,9 @@ var RollDate = (function () {
|
|
|
453
450
|
this.startWeekFromMonday = options.startWeekFromMonday;
|
|
454
451
|
this.monthsNames = options.monthsNames;
|
|
455
452
|
this.monthsShortNames = options.monthsShortNames;
|
|
453
|
+
this.weekDays = Array.isArray(options.weekDays) && options.weekDays.length === 7
|
|
454
|
+
? options.weekDays
|
|
455
|
+
: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
|
|
456
456
|
this.enableTime = Boolean(options.enableTime);
|
|
457
457
|
this.hasFooter = Boolean(options.enableTime || (options.footerButtons && options.footerButtons.length));
|
|
458
458
|
|
|
@@ -509,48 +509,77 @@ var RollDate = (function () {
|
|
|
509
509
|
}
|
|
510
510
|
|
|
511
511
|
#dayHeaders() {
|
|
512
|
-
const weekDays =
|
|
512
|
+
const weekDays = this.weekDays;
|
|
513
513
|
let header = '';
|
|
514
514
|
const startIndex = Number(this.startWeekFromMonday);
|
|
515
515
|
for (let i = startIndex; i < 7 + startIndex; i++) {
|
|
516
|
-
header += `<div class="RollDate__calendar__header__weekday">${
|
|
516
|
+
header += `<div class="RollDate__calendar__header__weekday">${weekDays[i % 7]}</div>`;
|
|
517
517
|
}
|
|
518
518
|
return header
|
|
519
519
|
}
|
|
520
520
|
|
|
521
|
-
|
|
521
|
+
#highlightDots(colors = [], maxVisible = 5) {
|
|
522
|
+
if (!colors.length) return ''
|
|
523
|
+
|
|
524
|
+
const visible = colors.slice(0, maxVisible);
|
|
525
|
+
const extra = colors.length - maxVisible;
|
|
526
|
+
const many = visible.length > 3;
|
|
527
|
+
const dotClass = many
|
|
528
|
+
? 'RollDate__calendar__day-dot RollDate__calendar__day-dot--compact'
|
|
529
|
+
: 'RollDate__calendar__day-dot';
|
|
530
|
+
|
|
531
|
+
const dots = visible.map(color => {
|
|
532
|
+
if (color) {
|
|
533
|
+
return `<span class="${dotClass} RollDate__calendar__day-dot--custom" style="--rd-highlight-dot:${color}"></span>`
|
|
534
|
+
}
|
|
535
|
+
return `<span class="${dotClass}"></span>`
|
|
536
|
+
}).join('');
|
|
537
|
+
|
|
538
|
+
const more = extra > 0
|
|
539
|
+
? `<span class="RollDate__calendar__day-dot RollDate__calendar__day-dot--more">+${extra}</span>`
|
|
540
|
+
: '';
|
|
541
|
+
|
|
542
|
+
return `<span class="RollDate__calendar__day-dots" aria-hidden="true">${dots}${more}</span>`
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
dates(array, selectedDates = [], selectType = 'single', getHighlight = () => null) {
|
|
522
546
|
let datesHtml = '';
|
|
523
547
|
const today = new Date();
|
|
524
548
|
|
|
525
549
|
for (let i = 0; i < array.length; i++) {
|
|
526
550
|
const date = array[i].date;
|
|
527
551
|
const disabledClass = array[i].disabled ? 'RollDate__calendar__day--disabled' : '';
|
|
552
|
+
const highlight = getHighlight(date);
|
|
553
|
+
const highlightDots = highlight ? this.#highlightDots(highlight.colors) : '';
|
|
528
554
|
const isToday = date.toDateString() === today.toDateString();
|
|
529
555
|
|
|
530
556
|
// Resolve selection class for current date cell.
|
|
531
557
|
let selectionClass = '';
|
|
532
558
|
if (selectType === 'single' || selectType === 'multi') {
|
|
559
|
+
const dayStamp = date.getTime();
|
|
533
560
|
const isSelected = selectedDates.some(
|
|
534
|
-
d => d.
|
|
561
|
+
d => new Date(d.getFullYear(), d.getMonth(), d.getDate()).getTime() === dayStamp
|
|
535
562
|
);
|
|
536
563
|
if (isSelected) selectionClass = 'RollDate__calendar__day--selected';
|
|
537
564
|
} else if (selectType === 'range' && selectedDates.length === 2) {
|
|
538
565
|
const [start, end] = selectedDates;
|
|
539
566
|
const time = date.getTime();
|
|
540
|
-
|
|
567
|
+
const startStamp = new Date(start.getFullYear(), start.getMonth(), start.getDate()).getTime();
|
|
568
|
+
const endStamp = new Date(end.getFullYear(), end.getMonth(), end.getDate()).getTime();
|
|
569
|
+
if (time === startStamp) {
|
|
541
570
|
selectionClass = 'RollDate__calendar__day--range-first';
|
|
542
|
-
} else if (time ===
|
|
571
|
+
} else if (time === endStamp) {
|
|
543
572
|
selectionClass = 'RollDate__calendar__day--range-last';
|
|
544
|
-
} else if (time >
|
|
573
|
+
} else if (time > startStamp && time < endStamp) {
|
|
545
574
|
selectionClass = 'RollDate__calendar__day--range-selected';
|
|
546
575
|
}
|
|
547
576
|
}
|
|
548
577
|
|
|
549
|
-
datesHtml += `<div class="RollDate__calendar__day ${disabledClass} ${isToday ? 'RollDate__calendar__day--today' : ''} ${selectionClass}"
|
|
578
|
+
datesHtml += `<div class="RollDate__calendar__day ${disabledClass} ${isToday ? 'RollDate__calendar__day--today' : ''} ${selectionClass}"
|
|
550
579
|
data-bind='["year", "month"]'
|
|
551
580
|
data-year="${date.getFullYear()}"
|
|
552
581
|
data-month="${date.getMonth()}"
|
|
553
|
-
data-day="${date.getDate()}">${date.getDate()}</div>`;
|
|
582
|
+
data-day="${date.getDate()}">${date.getDate()}${highlightDots}</div>`;
|
|
554
583
|
}
|
|
555
584
|
|
|
556
585
|
return datesHtml
|
|
@@ -610,495 +639,431 @@ var RollDate = (function () {
|
|
|
610
639
|
}
|
|
611
640
|
}
|
|
612
641
|
|
|
613
|
-
class Scroll {
|
|
614
|
-
#minScroll = null
|
|
615
|
-
#EDGE_TOP = 25
|
|
616
|
-
#EDGE_BOTTOM = 75
|
|
617
|
-
#offset
|
|
618
|
-
#baseOffset = 0
|
|
619
|
-
#blocked
|
|
620
|
-
#boundWheelHandler
|
|
621
|
-
#boundTouchStartHandler
|
|
622
|
-
#boundTouchMoveHandler
|
|
623
|
-
#boundTouchEndHandler
|
|
624
|
-
#isWheelAnimating = false
|
|
625
|
-
#edgeTriggeredInCurrentWheel = false
|
|
626
|
-
#lastEdgeTriggerAt = 0
|
|
627
|
-
#isTouchDragging = false
|
|
628
|
-
#touchLastY = 0
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
this
|
|
639
|
-
this
|
|
640
|
-
this.
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
this.#
|
|
648
|
-
this
|
|
649
|
-
this.#boundTouchStartHandler
|
|
650
|
-
this.#boundTouchMoveHandler
|
|
651
|
-
this.#boundTouchEndHandler
|
|
652
|
-
this.$body.addEventListener('
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
e.
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
const
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
this.#
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
const
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
this
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
this.#
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
this
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
this.
|
|
759
|
-
|
|
760
|
-
if (
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
}
|
|
799
|
-
|
|
800
|
-
if (this.#minScroll === 0 || this.blocked) return
|
|
801
|
-
if (this.#isWheelAnimating && this.#edgeTriggeredInCurrentWheel) return
|
|
802
|
-
|
|
803
|
-
const percent = Math.abs(this.offset / this.#minScroll) * 100;
|
|
804
|
-
const now = Date.now();
|
|
805
|
-
const EDGE_COOLDOWN_MS = 60;
|
|
806
|
-
|
|
807
|
-
if (now - this.#lastEdgeTriggerAt < EDGE_COOLDOWN_MS) return
|
|
808
|
-
|
|
809
|
-
if (percent < this.#EDGE_TOP) {
|
|
810
|
-
if (direction !== 'up') return
|
|
811
|
-
this.#lastEdgeTriggerAt = now;
|
|
812
|
-
this.#edgeTriggeredInCurrentWheel = true;
|
|
813
|
-
this.blocked = true;
|
|
814
|
-
this.updatePeriod('up');
|
|
815
|
-
} else if (percent > this.#EDGE_BOTTOM) {
|
|
816
|
-
if (direction !== 'down') return
|
|
817
|
-
this.#lastEdgeTriggerAt = now;
|
|
818
|
-
this.#edgeTriggeredInCurrentWheel = true;
|
|
819
|
-
this.blocked = true;
|
|
820
|
-
this.updatePeriod('down');
|
|
821
|
-
}
|
|
822
|
-
}
|
|
823
|
-
|
|
824
|
-
checkMinScroll() {
|
|
825
|
-
const scrollHeight = this.$scroll_block.clientHeight;
|
|
826
|
-
const bodyHeight = this.$body.clientHeight;
|
|
827
|
-
this.#minScroll = scrollHeight > bodyHeight ? -(scrollHeight - bodyHeight) : 0;
|
|
828
|
-
}
|
|
829
|
-
|
|
830
|
-
setBaseOffset(value = 0) {
|
|
831
|
-
this.#baseOffset = value;
|
|
832
|
-
this.apply();
|
|
833
|
-
}
|
|
834
|
-
|
|
835
|
-
resetMinScroll() {
|
|
836
|
-
this.#minScroll = null;
|
|
837
|
-
}
|
|
838
|
-
|
|
839
|
-
destroy() {
|
|
840
|
-
this.#cancelTouchMomentum();
|
|
841
|
-
if (this.#boundWheelHandler) {
|
|
842
|
-
this.$body.removeEventListener('wheel', this.#boundWheelHandler);
|
|
843
|
-
}
|
|
844
|
-
if (this.#boundTouchStartHandler) {
|
|
845
|
-
this.$body.removeEventListener('touchstart', this.#boundTouchStartHandler);
|
|
846
|
-
}
|
|
847
|
-
if (this.#boundTouchMoveHandler) {
|
|
848
|
-
this.$body.removeEventListener('touchmove', this.#boundTouchMoveHandler);
|
|
849
|
-
}
|
|
850
|
-
if (this.#boundTouchEndHandler) {
|
|
851
|
-
this.$body.removeEventListener('touchend', this.#boundTouchEndHandler);
|
|
852
|
-
this.$body.removeEventListener('touchcancel', this.#boundTouchEndHandler);
|
|
853
|
-
}
|
|
854
|
-
}
|
|
642
|
+
class Scroll {
|
|
643
|
+
#minScroll = null
|
|
644
|
+
#EDGE_TOP = 25
|
|
645
|
+
#EDGE_BOTTOM = 75
|
|
646
|
+
#offset
|
|
647
|
+
#baseOffset = 0
|
|
648
|
+
#blocked
|
|
649
|
+
#boundWheelHandler
|
|
650
|
+
#boundTouchStartHandler
|
|
651
|
+
#boundTouchMoveHandler
|
|
652
|
+
#boundTouchEndHandler
|
|
653
|
+
#isWheelAnimating = false
|
|
654
|
+
#edgeTriggeredInCurrentWheel = false
|
|
655
|
+
#lastEdgeTriggerAt = 0
|
|
656
|
+
#isTouchDragging = false
|
|
657
|
+
#touchLastY = 0
|
|
658
|
+
|
|
659
|
+
constructor(body, methods = {
|
|
660
|
+
dominant: () => console.error('Function "dominant" is not found'),
|
|
661
|
+
updatePeriod: () => console.error('Function "updatePeriod" is not found')
|
|
662
|
+
}) {
|
|
663
|
+
|
|
664
|
+
this.$body = body;
|
|
665
|
+
this.$scroll_block = this.$body.querySelector('.RollDate__calendar__scrollblock');
|
|
666
|
+
this.dominant = methods.dominant;
|
|
667
|
+
this.updatePeriod = methods.updatePeriod;
|
|
668
|
+
this.offset = 0;
|
|
669
|
+
this.init();
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
init() {
|
|
673
|
+
this.#boundWheelHandler = this.wheelHandler.bind(this);
|
|
674
|
+
this.$body.addEventListener('wheel', this.#boundWheelHandler, { passive: false });
|
|
675
|
+
this.#boundTouchStartHandler = this.touchStartHandler.bind(this);
|
|
676
|
+
this.#boundTouchMoveHandler = this.touchMoveHandler.bind(this);
|
|
677
|
+
this.#boundTouchEndHandler = this.touchEndHandler.bind(this);
|
|
678
|
+
this.$body.addEventListener('touchstart', this.#boundTouchStartHandler, { passive: true });
|
|
679
|
+
this.$body.addEventListener('touchmove', this.#boundTouchMoveHandler, { passive: false });
|
|
680
|
+
this.$body.addEventListener('touchend', this.#boundTouchEndHandler, { passive: true });
|
|
681
|
+
this.$body.addEventListener('touchcancel', this.#boundTouchEndHandler, { passive: true });
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
wheelHandler(e) {
|
|
685
|
+
e.preventDefault();
|
|
686
|
+
e.stopPropagation();
|
|
687
|
+
|
|
688
|
+
const rawDelta = e.deltaY * 0.85;
|
|
689
|
+
const steps = Math.abs(rawDelta) > 50 ? 20 : 1;
|
|
690
|
+
const stepSize = rawDelta / steps;
|
|
691
|
+
const currentWheelDirection = rawDelta > 0 ? 'down' : 'up';
|
|
692
|
+
this.#isWheelAnimating = true;
|
|
693
|
+
this.#edgeTriggeredInCurrentWheel = false;
|
|
694
|
+
|
|
695
|
+
let i = 0;
|
|
696
|
+
const animate = () => {
|
|
697
|
+
if (i >= steps) {
|
|
698
|
+
this.#isWheelAnimating = false;
|
|
699
|
+
this.#edgeTriggeredInCurrentWheel = false;
|
|
700
|
+
return
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
this.offset -= stepSize;
|
|
704
|
+
|
|
705
|
+
requestAnimationFrame(() => {
|
|
706
|
+
this.dominant();
|
|
707
|
+
this.checkEdge(currentWheelDirection);
|
|
708
|
+
i++;
|
|
709
|
+
animate();
|
|
710
|
+
});
|
|
711
|
+
};
|
|
712
|
+
|
|
713
|
+
animate();
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
touchStartHandler(e) {
|
|
717
|
+
if (!e.touches || e.touches.length !== 1) return
|
|
718
|
+
this.#isTouchDragging = true;
|
|
719
|
+
this.#touchLastY = e.touches[0].clientY;
|
|
720
|
+
this.#edgeTriggeredInCurrentWheel = false;
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
touchMoveHandler(e) {
|
|
724
|
+
if (!this.#isTouchDragging || !e.touches || e.touches.length !== 1) return
|
|
725
|
+
e.preventDefault();
|
|
726
|
+
|
|
727
|
+
const currentY = e.touches[0].clientY;
|
|
728
|
+
const deltaY = currentY - this.#touchLastY;
|
|
729
|
+
this.#touchLastY = currentY;
|
|
730
|
+
|
|
731
|
+
if (Math.abs(deltaY) < 1) return
|
|
732
|
+
|
|
733
|
+
this.offset += deltaY;
|
|
734
|
+
this.dominant();
|
|
735
|
+
|
|
736
|
+
const direction = deltaY < 0 ? 'down' : 'up';
|
|
737
|
+
this.checkEdge(direction);
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
touchEndHandler() {
|
|
741
|
+
this.#isTouchDragging = false;
|
|
742
|
+
this.#edgeTriggeredInCurrentWheel = false;
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
apply() {
|
|
746
|
+
this.$scroll_block.style.transform = `translateY(${this.offset + this.#baseOffset}px)`;
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
get offset() { return this.#offset }
|
|
750
|
+
set offset(value) {
|
|
751
|
+
|
|
752
|
+
this.#offset = value;
|
|
753
|
+
|
|
754
|
+
if ( this.#minScroll !== null ) {
|
|
755
|
+
if (value >= 0) this.#offset = 0;
|
|
756
|
+
else if (value <= this.#minScroll) this.#offset = this.#minScroll;
|
|
757
|
+
}
|
|
758
|
+
this.apply();
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
get blocked() { return this.#blocked }
|
|
762
|
+
set blocked(value) { this.#blocked = value; }
|
|
763
|
+
get minScroll() {
|
|
764
|
+
if (this.#minScroll === null) this.checkMinScroll();
|
|
765
|
+
return this.#minScroll ?? 0
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
checkEdge(direction) {
|
|
769
|
+
if (this.#minScroll === null) {
|
|
770
|
+
this.checkMinScroll();
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
if (this.#minScroll === 0 || this.blocked) return
|
|
774
|
+
if (this.#isWheelAnimating && this.#edgeTriggeredInCurrentWheel) return
|
|
775
|
+
|
|
776
|
+
const percent = Math.abs(this.offset / this.#minScroll) * 100;
|
|
777
|
+
const now = Date.now();
|
|
778
|
+
const EDGE_COOLDOWN_MS = 60;
|
|
779
|
+
|
|
780
|
+
if (now - this.#lastEdgeTriggerAt < EDGE_COOLDOWN_MS) return
|
|
781
|
+
|
|
782
|
+
if (percent < this.#EDGE_TOP) {
|
|
783
|
+
if (direction !== 'up') return
|
|
784
|
+
this.#lastEdgeTriggerAt = now;
|
|
785
|
+
this.#edgeTriggeredInCurrentWheel = true;
|
|
786
|
+
this.blocked = true;
|
|
787
|
+
this.updatePeriod('up');
|
|
788
|
+
} else if (percent > this.#EDGE_BOTTOM) {
|
|
789
|
+
if (direction !== 'down') return
|
|
790
|
+
this.#lastEdgeTriggerAt = now;
|
|
791
|
+
this.#edgeTriggeredInCurrentWheel = true;
|
|
792
|
+
this.blocked = true;
|
|
793
|
+
this.updatePeriod('down');
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
checkMinScroll() {
|
|
798
|
+
const scrollHeight = this.$scroll_block.clientHeight;
|
|
799
|
+
const bodyHeight = this.$body.clientHeight;
|
|
800
|
+
this.#minScroll = scrollHeight > bodyHeight ? -(scrollHeight - bodyHeight) : 0;
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
setBaseOffset(value = 0) {
|
|
804
|
+
this.#baseOffset = value;
|
|
805
|
+
this.apply();
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
resetMinScroll() {
|
|
809
|
+
this.#minScroll = null;
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
destroy() {
|
|
813
|
+
if (this.#boundWheelHandler) {
|
|
814
|
+
this.$body.removeEventListener('wheel', this.#boundWheelHandler);
|
|
815
|
+
}
|
|
816
|
+
if (this.#boundTouchStartHandler) {
|
|
817
|
+
this.$body.removeEventListener('touchstart', this.#boundTouchStartHandler);
|
|
818
|
+
}
|
|
819
|
+
if (this.#boundTouchMoveHandler) {
|
|
820
|
+
this.$body.removeEventListener('touchmove', this.#boundTouchMoveHandler);
|
|
821
|
+
}
|
|
822
|
+
if (this.#boundTouchEndHandler) {
|
|
823
|
+
this.$body.removeEventListener('touchend', this.#boundTouchEndHandler);
|
|
824
|
+
this.$body.removeEventListener('touchcancel', this.#boundTouchEndHandler);
|
|
825
|
+
}
|
|
826
|
+
}
|
|
855
827
|
}
|
|
856
828
|
|
|
857
|
-
class Virtualizer {
|
|
858
|
-
constructor(context) {
|
|
859
|
-
this.ctx = context;
|
|
860
|
-
}
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
const
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
if (this.ctx.data.
|
|
882
|
-
this.ctx.scroll.blocked = false;
|
|
883
|
-
return
|
|
884
|
-
}
|
|
885
|
-
}
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
new Date(
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
}
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
new Date(
|
|
932
|
-
this.ctx
|
|
933
|
-
);
|
|
934
|
-
}
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
newDates.
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
this.ctx.
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
this.
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
if (
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
const newHeight = block.scrollHeight;
|
|
1096
|
-
const heightDelta = oldHeight - newHeight;
|
|
1097
|
-
this.ctx.scroll.checkMinScroll();
|
|
1098
|
-
|
|
1099
|
-
if (isUp) this.ctx.scroll.offset -= heightDelta;
|
|
1100
|
-
else this.ctx.scroll.offset += heightDelta;
|
|
1101
|
-
}
|
|
829
|
+
class Virtualizer {
|
|
830
|
+
constructor(context) {
|
|
831
|
+
this.ctx = context;
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
update(direction) {
|
|
835
|
+
const period = this.ctx.period;
|
|
836
|
+
const isUp = direction === 'up';
|
|
837
|
+
|
|
838
|
+
// 🔑 Точна перевірка меж
|
|
839
|
+
if (isUp) {
|
|
840
|
+
if (period === 'year') {
|
|
841
|
+
if (this.ctx.data.up_date.getFullYear() <= this.ctx.options.minDate.getFullYear()) {
|
|
842
|
+
this.ctx.scroll.blocked = false;
|
|
843
|
+
return
|
|
844
|
+
}
|
|
845
|
+
} else {
|
|
846
|
+
if (this.ctx.data.up_date <= this.ctx.options.minDate) {
|
|
847
|
+
this.ctx.scroll.blocked = false;
|
|
848
|
+
return
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
} else {
|
|
852
|
+
if (period === 'year') {
|
|
853
|
+
if (this.ctx.data.down_date.getFullYear() >= this.ctx.options.maxDate.getFullYear()) {
|
|
854
|
+
this.ctx.scroll.blocked = false;
|
|
855
|
+
return
|
|
856
|
+
}
|
|
857
|
+
} else {
|
|
858
|
+
if (this.ctx.data.down_date >= this.ctx.options.maxDate) {
|
|
859
|
+
this.ctx.scroll.blocked = false;
|
|
860
|
+
return
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
const config = {
|
|
866
|
+
day: {
|
|
867
|
+
count: 35,
|
|
868
|
+
getNewItems: (upDate, downDate) => {
|
|
869
|
+
const newDates = [];
|
|
870
|
+
if (isUp) {
|
|
871
|
+
let newFirst = new Date(upDate);
|
|
872
|
+
newFirst.setDate(upDate.getDate() - 35);
|
|
873
|
+
if (newFirst < this.ctx.options.minDate) {
|
|
874
|
+
const minDate = this.ctx.options.minDate;
|
|
875
|
+
newFirst = adjustToWeekStart(
|
|
876
|
+
new Date(minDate.getFullYear(), minDate.getMonth(), 1),
|
|
877
|
+
this.ctx.options.startWeekFromMonday
|
|
878
|
+
);
|
|
879
|
+
}
|
|
880
|
+
for (let d = new Date(newFirst); d < upDate; d.setDate(d.getDate() + 1)) {
|
|
881
|
+
newDates.push({
|
|
882
|
+
date: new Date(d),
|
|
883
|
+
disabled: this.ctx?.options?.minDate > new Date(d) || false
|
|
884
|
+
});
|
|
885
|
+
}
|
|
886
|
+
return { items: newDates, newBound: newFirst }
|
|
887
|
+
} else {
|
|
888
|
+
let newLast = new Date(downDate);
|
|
889
|
+
newLast.setDate(downDate.getDate() + 35);
|
|
890
|
+
const firstNew = new Date(downDate);
|
|
891
|
+
firstNew.setDate(downDate.getDate() + 1);
|
|
892
|
+
|
|
893
|
+
if (newLast > this.ctx.options.maxDate) {
|
|
894
|
+
const maxDate = this.ctx.options.maxDate;
|
|
895
|
+
newLast = adjustToWeekEnd(
|
|
896
|
+
new Date(maxDate.getFullYear(), maxDate.getMonth() + 1, 0),
|
|
897
|
+
this.ctx.options.startWeekFromMonday
|
|
898
|
+
);
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
for (let d = firstNew; d <= newLast; d.setDate(d.getDate() + 1)) {
|
|
902
|
+
newDates.push({
|
|
903
|
+
date: new Date(d),
|
|
904
|
+
disabled: this.ctx?.options?.maxDate < new Date(d) || false
|
|
905
|
+
});
|
|
906
|
+
}
|
|
907
|
+
return { items: newDates, newBound: newLast }
|
|
908
|
+
}
|
|
909
|
+
},
|
|
910
|
+
render: dates => this.ctx.render.dates(
|
|
911
|
+
dates,
|
|
912
|
+
this.ctx.selectedDates,
|
|
913
|
+
this.ctx.options.selectType
|
|
914
|
+
),
|
|
915
|
+
block: this.ctx.dom.$days_block
|
|
916
|
+
},
|
|
917
|
+
month: {
|
|
918
|
+
count: 36,
|
|
919
|
+
ctx: this.ctx,
|
|
920
|
+
getNewItems: function (upDate, downDate) {
|
|
921
|
+
const newDates = [];
|
|
922
|
+
const isMonthDisabled = (year, month) => {
|
|
923
|
+
const date = new Date(year, month, 1);
|
|
924
|
+
const monthEnd = new Date(year, month + 1, 0);
|
|
925
|
+
if (this.ctx.options.minDate && monthEnd < this.ctx.options.minDate) return true
|
|
926
|
+
if (this.ctx.options.maxDate && date > this.ctx.options.maxDate) return true
|
|
927
|
+
return false
|
|
928
|
+
};
|
|
929
|
+
|
|
930
|
+
if (isUp) {
|
|
931
|
+
// Генеруємо повними роками (кратно 12), щоб Jan завжди був ліворуч у сітці.
|
|
932
|
+
let curr = new Date(upDate);
|
|
933
|
+
for (let i = 0; i < this.count; i++) {
|
|
934
|
+
curr.setMonth(curr.getMonth() - 1);
|
|
935
|
+
newDates.unshift({
|
|
936
|
+
date: new Date(curr.getFullYear(), curr.getMonth(), 1),
|
|
937
|
+
disabled: isMonthDisabled(curr.getFullYear(), curr.getMonth())
|
|
938
|
+
});
|
|
939
|
+
}
|
|
940
|
+
const newBound = newDates.length
|
|
941
|
+
? newDates[0].date
|
|
942
|
+
: upDate;
|
|
943
|
+
return { items: newDates, newBound }
|
|
944
|
+
} else {
|
|
945
|
+
// Генеруємо повними роками (кратно 12), щоб структура рядків не "плавала".
|
|
946
|
+
let curr = new Date(downDate);
|
|
947
|
+
curr.setMonth(curr.getMonth() + 1);
|
|
948
|
+
for (let i = 0; i < this.count; i++) {
|
|
949
|
+
newDates.push({
|
|
950
|
+
date: new Date(curr.getFullYear(), curr.getMonth(), 1),
|
|
951
|
+
disabled: isMonthDisabled(curr.getFullYear(), curr.getMonth())
|
|
952
|
+
});
|
|
953
|
+
curr.setMonth(curr.getMonth() + 1);
|
|
954
|
+
}
|
|
955
|
+
const newBound = newDates.length
|
|
956
|
+
? newDates[newDates.length - 1].date
|
|
957
|
+
: downDate;
|
|
958
|
+
return { items: newDates, newBound }
|
|
959
|
+
}
|
|
960
|
+
},
|
|
961
|
+
render: months => this.ctx.render.months(months),
|
|
962
|
+
block: this.ctx.dom.$months_block
|
|
963
|
+
},
|
|
964
|
+
year: {
|
|
965
|
+
count: 16,
|
|
966
|
+
ctx: this.ctx,
|
|
967
|
+
getNewItems: function (upDate, downDate) {
|
|
968
|
+
const newDates = [];
|
|
969
|
+
if (isUp) {
|
|
970
|
+
// 🔑 Генерація СТРОГО перед upDate.getFullYear()
|
|
971
|
+
const startYear = upDate.getFullYear() - this.count;
|
|
972
|
+
for (let y = startYear; y < upDate.getFullYear(); y++) {
|
|
973
|
+
if (y < this.ctx.options.minDate.getFullYear()) continue
|
|
974
|
+
newDates.push({
|
|
975
|
+
date: new Date(y, 0, 1),
|
|
976
|
+
disabled: this.ctx.options.minDate.getFullYear() > y
|
|
977
|
+
});
|
|
978
|
+
}
|
|
979
|
+
const newBound = newDates.length
|
|
980
|
+
? newDates[0].date
|
|
981
|
+
: upDate;
|
|
982
|
+
return { items: newDates, newBound }
|
|
983
|
+
} else {
|
|
984
|
+
// 🔑 Генерація СТРОГО після downDate.getFullYear()
|
|
985
|
+
const startYear = downDate.getFullYear() + 1;
|
|
986
|
+
for (let y = startYear; y < startYear + this.count; y++) {
|
|
987
|
+
if (y > this.ctx.options.maxDate.getFullYear()) break
|
|
988
|
+
newDates.push({
|
|
989
|
+
date: new Date(y, 0, 1),
|
|
990
|
+
disabled: this.ctx.options.maxDate.getFullYear() < y
|
|
991
|
+
});
|
|
992
|
+
}
|
|
993
|
+
const newBound = newDates.length
|
|
994
|
+
? newDates[newDates.length - 1].date
|
|
995
|
+
: downDate;
|
|
996
|
+
return { items: newDates, newBound }
|
|
997
|
+
}
|
|
998
|
+
},
|
|
999
|
+
render: years => this.ctx.render.years(years),
|
|
1000
|
+
block: this.ctx.dom.$years_block
|
|
1001
|
+
}
|
|
1002
|
+
}[period];
|
|
1003
|
+
|
|
1004
|
+
if (!config) return
|
|
1005
|
+
|
|
1006
|
+
const { items, newBound } = config.getNewItems(this.ctx.data.up_date, this.ctx.data.down_date);
|
|
1007
|
+
|
|
1008
|
+
if (items.length) {
|
|
1009
|
+
config.block.insertAdjacentHTML(
|
|
1010
|
+
isUp ? 'afterbegin' : 'beforeend',
|
|
1011
|
+
config.render(items)
|
|
1012
|
+
);
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
this.ctx.data[`${direction}_date`] = newBound;
|
|
1016
|
+
this.ctx.observe.un();
|
|
1017
|
+
const trimSize = period === 'year'
|
|
1018
|
+
? Math.min(4, items.length)
|
|
1019
|
+
: items.length;
|
|
1020
|
+
this.trim(direction, trimSize);
|
|
1021
|
+
this.ctx.observe.on(period);
|
|
1022
|
+
this.ctx.scroll.blocked = false;
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1025
|
+
trim(direction, remove) {
|
|
1026
|
+
const period = this.ctx.period;
|
|
1027
|
+
const block = this.ctx.dom[`$${period}s_block`];
|
|
1028
|
+
const isUp = direction === 'up';
|
|
1029
|
+
const minItemsInDom = period === 'year' ? 28 : 0;
|
|
1030
|
+
|
|
1031
|
+
const oldHeight = block.scrollHeight;
|
|
1032
|
+
const items = Array.from(block.querySelectorAll(`.RollDate__calendar__${period}`));
|
|
1033
|
+
|
|
1034
|
+
if (!remove || items.length <= remove * 2) return
|
|
1035
|
+
if (minItemsInDom && (items.length - remove) < minItemsInDom) return
|
|
1036
|
+
|
|
1037
|
+
if (isUp) {
|
|
1038
|
+
items.slice(-remove).forEach(item => item.remove());
|
|
1039
|
+
} else {
|
|
1040
|
+
items.slice(0, remove).forEach(item => item.remove());
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
// Перераховуємо межі тільки з поточного DOM після trim, щоб уникнути "миготіння" і зациклення дозагрузки.
|
|
1044
|
+
const currentItems = Array.from(block.querySelectorAll(`.RollDate__calendar__${period}`));
|
|
1045
|
+
const firstItem = currentItems[0];
|
|
1046
|
+
const lastItem = currentItems[currentItems.length - 1];
|
|
1047
|
+
const toDate = (el) => {
|
|
1048
|
+
if (!el) return null
|
|
1049
|
+
const year = Number(el.dataset.year);
|
|
1050
|
+
const month = Number(el.dataset.month) || 0;
|
|
1051
|
+
const day = period === 'day' ? Number(el.dataset.day) : 1;
|
|
1052
|
+
return new Date(year, month, day)
|
|
1053
|
+
};
|
|
1054
|
+
|
|
1055
|
+
const newUpDate = toDate(firstItem);
|
|
1056
|
+
const newDownDate = toDate(lastItem);
|
|
1057
|
+
if (newUpDate) this.ctx.data.up_date = newUpDate;
|
|
1058
|
+
if (newDownDate) this.ctx.data.down_date = newDownDate;
|
|
1059
|
+
|
|
1060
|
+
const newHeight = block.scrollHeight;
|
|
1061
|
+
const heightDelta = oldHeight - newHeight;
|
|
1062
|
+
this.ctx.scroll.checkMinScroll();
|
|
1063
|
+
|
|
1064
|
+
if (isUp) this.ctx.scroll.offset -= heightDelta;
|
|
1065
|
+
else this.ctx.scroll.offset += heightDelta;
|
|
1066
|
+
}
|
|
1102
1067
|
}
|
|
1103
1068
|
|
|
1104
1069
|
const ITEM_HEIGHT = 28;
|
|
@@ -1222,6 +1187,12 @@ var RollDate = (function () {
|
|
|
1222
1187
|
}
|
|
1223
1188
|
}
|
|
1224
1189
|
|
|
1190
|
+
#commitTime() {
|
|
1191
|
+
this.#readColumns();
|
|
1192
|
+
this.#updateDisplay();
|
|
1193
|
+
this.onChange(this.getTime());
|
|
1194
|
+
}
|
|
1195
|
+
|
|
1225
1196
|
#createColumn(unit, values, initial) {
|
|
1226
1197
|
const field = this.root.querySelector(`[data-unit="${unit}"]`);
|
|
1227
1198
|
const viewport = field.querySelector('.RollDate__time__viewport');
|
|
@@ -1250,6 +1221,7 @@ var RollDate = (function () {
|
|
|
1250
1221
|
if (column._lastIndex !== index) {
|
|
1251
1222
|
if (column._lastIndex !== undefined) {
|
|
1252
1223
|
hapticTick(this.hapticFeedback);
|
|
1224
|
+
this.#commitTime();
|
|
1253
1225
|
}
|
|
1254
1226
|
column._lastIndex = index;
|
|
1255
1227
|
}
|
|
@@ -1274,10 +1246,9 @@ var RollDate = (function () {
|
|
|
1274
1246
|
column.snap = () => {
|
|
1275
1247
|
const index = column.indexFromOffset();
|
|
1276
1248
|
column.offset = -index * ITEM_HEIGHT;
|
|
1249
|
+
column._lastIndex = index;
|
|
1277
1250
|
column.apply(true);
|
|
1278
|
-
this.#
|
|
1279
|
-
this.#updateDisplay();
|
|
1280
|
-
this.onChange(this.getTime());
|
|
1251
|
+
this.#commitTime();
|
|
1281
1252
|
};
|
|
1282
1253
|
|
|
1283
1254
|
column.scrollToValue = (value, animate = true) => {
|
|
@@ -1291,9 +1262,6 @@ var RollDate = (function () {
|
|
|
1291
1262
|
const index = Math.min(values.length - 1, Math.max(0, column.indexFromOffset() + delta));
|
|
1292
1263
|
column.offset = -index * ITEM_HEIGHT;
|
|
1293
1264
|
column.apply(true);
|
|
1294
|
-
this.#readColumns();
|
|
1295
|
-
this.#updateDisplay();
|
|
1296
|
-
this.onChange(this.getTime());
|
|
1297
1265
|
};
|
|
1298
1266
|
|
|
1299
1267
|
column.scrollToValue(initial, false);
|
|
@@ -1407,6 +1375,7 @@ var RollDate = (function () {
|
|
|
1407
1375
|
#selectedDates = []
|
|
1408
1376
|
#firstOpen = true
|
|
1409
1377
|
#disabledDateStamps = new Set()
|
|
1378
|
+
#highlightDateMap = new Map()
|
|
1410
1379
|
#docClickHandler = null
|
|
1411
1380
|
#openTriggers = []
|
|
1412
1381
|
|
|
@@ -1422,12 +1391,36 @@ var RollDate = (function () {
|
|
|
1422
1391
|
}
|
|
1423
1392
|
|
|
1424
1393
|
#normalizeDateInput(dateLike) {
|
|
1394
|
+
if (dateLike instanceof Date && !Number.isNaN(dateLike.getTime())) {
|
|
1395
|
+
return new Date(dateLike.getFullYear(), dateLike.getMonth(), dateLike.getDate())
|
|
1396
|
+
}
|
|
1397
|
+
|
|
1398
|
+
if (typeof dateLike === 'string') {
|
|
1399
|
+
const clean = dateLike.trim();
|
|
1400
|
+
if (!clean) return null
|
|
1401
|
+
|
|
1402
|
+
const valueSep = clean.match(/[-/.]/)?.[0];
|
|
1403
|
+
const formatSep = this.options.dateFormat.match(/[-/.]/)?.[0];
|
|
1404
|
+
const format = valueSep && formatSep && valueSep !== formatSep
|
|
1405
|
+
? 'auto'
|
|
1406
|
+
: this.options.dateFormat;
|
|
1407
|
+
|
|
1408
|
+
const date = parseDate(clean, format);
|
|
1409
|
+
if (!(date instanceof Date) || Number.isNaN(date.getTime())) {
|
|
1410
|
+
const fallback = parseDate(clean, 'auto');
|
|
1411
|
+
if (!(fallback instanceof Date) || Number.isNaN(fallback.getTime())) return null
|
|
1412
|
+
return new Date(fallback.getFullYear(), fallback.getMonth(), fallback.getDate())
|
|
1413
|
+
}
|
|
1414
|
+
|
|
1415
|
+
return new Date(date.getFullYear(), date.getMonth(), date.getDate())
|
|
1416
|
+
}
|
|
1417
|
+
|
|
1425
1418
|
const date = checkDateFormat(dateLike, this.options.dateFormat);
|
|
1426
1419
|
if (!(date instanceof Date) || Number.isNaN(date.getTime())) return null
|
|
1427
1420
|
return new Date(date.getFullYear(), date.getMonth(), date.getDate())
|
|
1428
1421
|
}
|
|
1429
1422
|
|
|
1430
|
-
#
|
|
1423
|
+
#buildDateStampSet(dates = []) {
|
|
1431
1424
|
const set = new Set();
|
|
1432
1425
|
dates.forEach(dateLike => {
|
|
1433
1426
|
const normalized = this.#normalizeDateInput(dateLike);
|
|
@@ -1436,6 +1429,85 @@ var RollDate = (function () {
|
|
|
1436
1429
|
return set
|
|
1437
1430
|
}
|
|
1438
1431
|
|
|
1432
|
+
#buildDisabledDateSet(dates = []) {
|
|
1433
|
+
return this.#buildDateStampSet(dates)
|
|
1434
|
+
}
|
|
1435
|
+
|
|
1436
|
+
#buildHighlightDateMap(dates = []) {
|
|
1437
|
+
const map = new Map();
|
|
1438
|
+
if (!Array.isArray(dates)) return map
|
|
1439
|
+
|
|
1440
|
+
dates.forEach(entry => {
|
|
1441
|
+
const item = this.#normalizeHighlightEntry(entry);
|
|
1442
|
+
if (!item) return
|
|
1443
|
+
|
|
1444
|
+
const existing = map.get(item.stamp) || [];
|
|
1445
|
+
map.set(item.stamp, existing.concat(item.colors));
|
|
1446
|
+
});
|
|
1447
|
+
|
|
1448
|
+
return map
|
|
1449
|
+
}
|
|
1450
|
+
|
|
1451
|
+
#normalizeHighlightEntry(entry) {
|
|
1452
|
+
if (entry == null) return null
|
|
1453
|
+
|
|
1454
|
+
if (typeof entry === 'string' || entry instanceof Date) {
|
|
1455
|
+
const normalized = this.#normalizeDateInput(entry);
|
|
1456
|
+
if (!normalized) return null
|
|
1457
|
+
return {
|
|
1458
|
+
stamp: this.#toDateStamp(normalized),
|
|
1459
|
+
colors: [null]
|
|
1460
|
+
}
|
|
1461
|
+
}
|
|
1462
|
+
|
|
1463
|
+
if (typeof entry === 'object' && entry.date != null) {
|
|
1464
|
+
const normalized = this.#normalizeDateInput(entry.date);
|
|
1465
|
+
if (!normalized) return null
|
|
1466
|
+
const stamp = this.#toDateStamp(normalized);
|
|
1467
|
+
|
|
1468
|
+
if (Array.isArray(entry.colors)) {
|
|
1469
|
+
const colors = entry.colors.map(color => {
|
|
1470
|
+
if (color == null || color === '') return null
|
|
1471
|
+
return this.#sanitizeHighlightColor(color)
|
|
1472
|
+
}).filter(color => color !== undefined);
|
|
1473
|
+
|
|
1474
|
+
if (colors.length) return { stamp, colors }
|
|
1475
|
+
}
|
|
1476
|
+
|
|
1477
|
+
if (entry.color != null && entry.color !== '') {
|
|
1478
|
+
const color = this.#sanitizeHighlightColor(entry.color);
|
|
1479
|
+
if (color) return { stamp, colors: [color] }
|
|
1480
|
+
}
|
|
1481
|
+
|
|
1482
|
+
return { stamp, colors: [null] }
|
|
1483
|
+
}
|
|
1484
|
+
|
|
1485
|
+
return null
|
|
1486
|
+
}
|
|
1487
|
+
|
|
1488
|
+
#sanitizeHighlightColor(color) {
|
|
1489
|
+
if (typeof color !== 'string') return null
|
|
1490
|
+
const value = color.trim();
|
|
1491
|
+
if (!value) return null
|
|
1492
|
+
|
|
1493
|
+
if (/^#([0-9a-f]{3}|[0-9a-f]{6}|[0-9a-f]{8})$/i.test(value)) return value
|
|
1494
|
+
if (/^(rgb|rgba|hsl|hsla)\([^)]+\)$/i.test(value)) return value
|
|
1495
|
+
if (/^var\(--[\w-]+\)$/.test(value)) return value
|
|
1496
|
+
|
|
1497
|
+
return null
|
|
1498
|
+
}
|
|
1499
|
+
|
|
1500
|
+
#highlightMapToOptionsArray() {
|
|
1501
|
+
return [...this.#highlightDateMap.entries()].map(([stamp, colors]) => {
|
|
1502
|
+
const date = new Date(stamp);
|
|
1503
|
+
|
|
1504
|
+
if (colors.length === 1 && colors[0] === null) return date
|
|
1505
|
+
if (colors.length === 1 && colors[0]) return { date, color: colors[0] }
|
|
1506
|
+
|
|
1507
|
+
return { date, colors }
|
|
1508
|
+
})
|
|
1509
|
+
}
|
|
1510
|
+
|
|
1439
1511
|
#disableInputAssist(input) {
|
|
1440
1512
|
if (!input || input.tagName !== 'INPUT') return
|
|
1441
1513
|
input.setAttribute('autocomplete', 'off');
|
|
@@ -1448,6 +1520,16 @@ var RollDate = (function () {
|
|
|
1448
1520
|
return this.#disabledDateStamps.has(this.#toDateStamp(date))
|
|
1449
1521
|
}
|
|
1450
1522
|
|
|
1523
|
+
#isDateHighlighted(date) {
|
|
1524
|
+
return this.#highlightDateMap.has(this.#toDateStamp(date))
|
|
1525
|
+
}
|
|
1526
|
+
|
|
1527
|
+
#getHighlightMeta(date) {
|
|
1528
|
+
const colors = this.#highlightDateMap.get(this.#toDateStamp(date));
|
|
1529
|
+
if (!colors?.length) return null
|
|
1530
|
+
return { colors }
|
|
1531
|
+
}
|
|
1532
|
+
|
|
1451
1533
|
#notifySelectionChange() {
|
|
1452
1534
|
if (this.options.selectType === 'single') {
|
|
1453
1535
|
this.options.selectDate(this.#selectedDates[0] || null);
|
|
@@ -1523,6 +1605,69 @@ var RollDate = (function () {
|
|
|
1523
1605
|
}
|
|
1524
1606
|
}
|
|
1525
1607
|
|
|
1608
|
+
#mountRangePresets() {
|
|
1609
|
+
const presets = Array.isArray(this.options.rangePresets)
|
|
1610
|
+
? this.options.rangePresets
|
|
1611
|
+
: [];
|
|
1612
|
+
|
|
1613
|
+
if (this.options.selectType !== 'range' || !presets.length) return
|
|
1614
|
+
|
|
1615
|
+
let bar = this.$container.querySelector('.RollDate__presets');
|
|
1616
|
+
if (!bar) {
|
|
1617
|
+
bar = document.createElement('div');
|
|
1618
|
+
bar.className = 'RollDate__presets';
|
|
1619
|
+
const footer = this.$container.querySelector('.RollDate__footer');
|
|
1620
|
+
const calendar = this.$container.querySelector('.RollDate__calendar');
|
|
1621
|
+
if (footer) footer.before(bar);
|
|
1622
|
+
else if (calendar) calendar.after(bar);
|
|
1623
|
+
}
|
|
1624
|
+
|
|
1625
|
+
bar.innerHTML = '';
|
|
1626
|
+
presets.forEach(preset => {
|
|
1627
|
+
if (!preset?.label || typeof preset.getRange !== 'function') return
|
|
1628
|
+
|
|
1629
|
+
const btn = document.createElement('button');
|
|
1630
|
+
btn.type = 'button';
|
|
1631
|
+
btn.className = 'RollDate__presets__button';
|
|
1632
|
+
btn.textContent = preset.label;
|
|
1633
|
+
btn.addEventListener('click', (e) => {
|
|
1634
|
+
e.preventDefault();
|
|
1635
|
+
e.stopPropagation();
|
|
1636
|
+
this.#applyRangePreset(preset);
|
|
1637
|
+
});
|
|
1638
|
+
bar.append(btn);
|
|
1639
|
+
});
|
|
1640
|
+
|
|
1641
|
+
if (bar.childElementCount) {
|
|
1642
|
+
this.$container.classList.add('RollDate__has-presets');
|
|
1643
|
+
}
|
|
1644
|
+
}
|
|
1645
|
+
|
|
1646
|
+
#applyRangePreset(preset) {
|
|
1647
|
+
const result = preset.getRange(this);
|
|
1648
|
+
if (!Array.isArray(result) || result.length < 2) return
|
|
1649
|
+
|
|
1650
|
+
const normalized = result
|
|
1651
|
+
.slice(0, 2)
|
|
1652
|
+
.map(dateLike => this.#normalizeDateInput(dateLike))
|
|
1653
|
+
.filter(Boolean)
|
|
1654
|
+
.map(date => this.#clampDateToRange(date, this.options.minDate, this.options.maxDate))
|
|
1655
|
+
.filter(date => !this.#isDateDisabled(date));
|
|
1656
|
+
|
|
1657
|
+
if (normalized.length < 2) return
|
|
1658
|
+
|
|
1659
|
+
let [start, end] = normalized;
|
|
1660
|
+
if (start > end) [start, end] = [end, start];
|
|
1661
|
+
|
|
1662
|
+
this.#selectedDates = [
|
|
1663
|
+
this.#applyTimeToDate(start),
|
|
1664
|
+
this.#applyTimeToDate(end)
|
|
1665
|
+
];
|
|
1666
|
+
this.#notifySelectionChange();
|
|
1667
|
+
this.#updateInputValue();
|
|
1668
|
+
this.#paintRangeSelection();
|
|
1669
|
+
}
|
|
1670
|
+
|
|
1526
1671
|
#initTimePicker() {
|
|
1527
1672
|
const start = this.options.startDate;
|
|
1528
1673
|
this.timePicker = new TimePicker(this.dom.$time, {
|
|
@@ -1579,11 +1724,13 @@ var RollDate = (function () {
|
|
|
1579
1724
|
onViewChange: () => {},
|
|
1580
1725
|
onHoverDate: () => {},
|
|
1581
1726
|
disabledDates: [],
|
|
1727
|
+
highlightDates: [],
|
|
1582
1728
|
closeOnSelect: true,
|
|
1583
1729
|
enableTime: false,
|
|
1584
1730
|
use12Hour: false,
|
|
1585
1731
|
timeStep: 1,
|
|
1586
1732
|
footerButtons: [],
|
|
1733
|
+
rangePresets: [],
|
|
1587
1734
|
hapticFeedback: true,
|
|
1588
1735
|
...options
|
|
1589
1736
|
};
|
|
@@ -1626,6 +1773,7 @@ var RollDate = (function () {
|
|
|
1626
1773
|
this.options.maxDate
|
|
1627
1774
|
);
|
|
1628
1775
|
this.#disabledDateStamps = this.#buildDisabledDateSet(this.options.disabledDates);
|
|
1776
|
+
this.#highlightDateMap = this.#buildHighlightDateMap(this.options.highlightDates);
|
|
1629
1777
|
if (this.#isDateDisabled(this.options.startDate)) {
|
|
1630
1778
|
this.options.startDate = new Date(this.options.minDate);
|
|
1631
1779
|
}
|
|
@@ -1698,6 +1846,7 @@ var RollDate = (function () {
|
|
|
1698
1846
|
};
|
|
1699
1847
|
|
|
1700
1848
|
this.#initFooterButtons();
|
|
1849
|
+
this.#mountRangePresets();
|
|
1701
1850
|
if (this.options.enableTime && this.dom.$time) {
|
|
1702
1851
|
this.#initTimePicker();
|
|
1703
1852
|
}
|
|
@@ -1758,7 +1907,23 @@ var RollDate = (function () {
|
|
|
1758
1907
|
this.$container.style.zIndex = '10000';
|
|
1759
1908
|
}
|
|
1760
1909
|
|
|
1761
|
-
#updateView() {
|
|
1910
|
+
#updateView(viewNumberOrOptions, maybeOptions) {
|
|
1911
|
+
let options = {};
|
|
1912
|
+
|
|
1913
|
+
if (typeof viewNumberOrOptions === 'object' && viewNumberOrOptions !== null) {
|
|
1914
|
+
options = viewNumberOrOptions;
|
|
1915
|
+
} else {
|
|
1916
|
+
if (typeof viewNumberOrOptions === 'number') {
|
|
1917
|
+
this.#viewNumber = viewNumberOrOptions;
|
|
1918
|
+
}
|
|
1919
|
+
if (typeof maybeOptions === 'object' && maybeOptions !== null) {
|
|
1920
|
+
options = maybeOptions;
|
|
1921
|
+
}
|
|
1922
|
+
}
|
|
1923
|
+
|
|
1924
|
+
const preserveScroll = options.preserveScroll === true;
|
|
1925
|
+
const savedOffset = preserveScroll ? this.scroll.offset : null;
|
|
1926
|
+
|
|
1762
1927
|
this.#updateHeader();
|
|
1763
1928
|
this.scroll.blocked = false;
|
|
1764
1929
|
this.observe.un(this.period);
|
|
@@ -1770,7 +1935,8 @@ var RollDate = (function () {
|
|
|
1770
1935
|
this.dom.$days_block.innerHTML = this.render.dates(
|
|
1771
1936
|
days,
|
|
1772
1937
|
this.#selectedDates,
|
|
1773
|
-
this.options.selectType
|
|
1938
|
+
this.options.selectType,
|
|
1939
|
+
date => this.#getHighlightMeta(date)
|
|
1774
1940
|
);
|
|
1775
1941
|
break
|
|
1776
1942
|
|
|
@@ -1779,41 +1945,49 @@ var RollDate = (function () {
|
|
|
1779
1945
|
this.dom[`$${this.period}s_block`].innerHTML = this.render[`${this.period}s`](dates);
|
|
1780
1946
|
}
|
|
1781
1947
|
|
|
1782
|
-
const
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1948
|
+
const applyScroll = () => {
|
|
1949
|
+
const $scrollBlock = this.dom.$body.querySelector('.RollDate__calendar__scrollblock');
|
|
1950
|
+
const bodyHeight = this.dom.$body.clientHeight;
|
|
1951
|
+
const blockHeight = $scrollBlock.clientHeight;
|
|
1952
|
+
|
|
1953
|
+
if (blockHeight <= bodyHeight) {
|
|
1954
|
+
this.scroll.setBaseOffset(bodyHeight - blockHeight);
|
|
1955
|
+
this.scroll.offset = 0;
|
|
1956
|
+
return
|
|
1957
|
+
}
|
|
1958
|
+
|
|
1959
|
+
this.scroll.setBaseOffset(0);
|
|
1960
|
+
this.scroll.resetMinScroll();
|
|
1961
|
+
|
|
1962
|
+
if (preserveScroll && savedOffset != null) {
|
|
1963
|
+
this.scroll.offset = Math.max(savedOffset, this.scroll.minScroll);
|
|
1964
|
+
return
|
|
1965
|
+
}
|
|
1966
|
+
|
|
1967
|
+
const selectors = {
|
|
1968
|
+
day: `.RollDate__calendar__day[data-year="${this.data.current_year}"][data-month="${this.data.current_month}"]`,
|
|
1969
|
+
month: `.RollDate__calendar__month[data-year="${this.data.current_year}"][data-month="0"]`,
|
|
1970
|
+
year: `.RollDate__calendar__year[data-decade="${this.data.current_decade}"][data-year="${this.data.current_year}"]`
|
|
1793
1971
|
};
|
|
1794
|
-
$firstEl = this.$container.querySelector(
|
|
1795
|
-
|
|
1972
|
+
let $firstEl = this.$container.querySelector(selectors[this.period]);
|
|
1973
|
+
if (!$firstEl) {
|
|
1974
|
+
const fallbacks = {
|
|
1975
|
+
day: `.RollDate__calendar__day[data-year="${this.data.current_year}"]`,
|
|
1976
|
+
month: `.RollDate__calendar__month[data-year="${this.data.current_year}"]`,
|
|
1977
|
+
year: `.RollDate__calendar__year[data-decade="${this.data.current_decade}"]`
|
|
1978
|
+
};
|
|
1979
|
+
$firstEl = this.$container.querySelector(fallbacks[this.period]);
|
|
1980
|
+
}
|
|
1796
1981
|
|
|
1797
|
-
|
|
1798
|
-
setTimeout(() => {
|
|
1799
|
-
const $scrollBlock = this.dom.$body.querySelector('.RollDate__calendar__scrollblock');
|
|
1800
|
-
const bodyHeight = this.dom.$body.clientHeight;
|
|
1801
|
-
const blockHeight = $scrollBlock.clientHeight;
|
|
1982
|
+
if (!$firstEl) return
|
|
1802
1983
|
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1984
|
+
const containerRect = $scrollBlock.getBoundingClientRect();
|
|
1985
|
+
const firstRect = $firstEl.getBoundingClientRect();
|
|
1986
|
+
const firstRectOffset = -(firstRect.top - containerRect.top);
|
|
1987
|
+
this.scroll.offset = Math.max(firstRectOffset, this.scroll.minScroll);
|
|
1988
|
+
};
|
|
1808
1989
|
|
|
1809
|
-
|
|
1810
|
-
const containerRect = $scrollBlock.getBoundingClientRect();
|
|
1811
|
-
const firstRect = $firstEl.getBoundingClientRect();
|
|
1812
|
-
const firstRectOffset = -(firstRect.top - containerRect.top);
|
|
1813
|
-
const minScroll = this.scroll.minScroll;
|
|
1814
|
-
this.scroll.offset = Math.max(firstRectOffset, minScroll);
|
|
1815
|
-
}, 0);
|
|
1816
|
-
}
|
|
1990
|
+
setTimeout(applyScroll, 0);
|
|
1817
1991
|
|
|
1818
1992
|
this.observe.on(this.period, item => {
|
|
1819
1993
|
const cond = JSON.parse(item.dataset.bind).every(data => Number(item.dataset[data]) === this.data[`current_${data}`]);
|
|
@@ -1967,9 +2141,10 @@ var RollDate = (function () {
|
|
|
1967
2141
|
}
|
|
1968
2142
|
|
|
1969
2143
|
if (this.options.selectType === 'range') {
|
|
2144
|
+
const clickStamp = this.#toDateStamp(date);
|
|
1970
2145
|
const isClickInRange = this.#selectedDates.length === 2 &&
|
|
1971
|
-
|
|
1972
|
-
|
|
2146
|
+
clickStamp >= this.#toDateStamp(this.#selectedDates[0]) &&
|
|
2147
|
+
clickStamp <= this.#toDateStamp(this.#selectedDates[1]);
|
|
1973
2148
|
|
|
1974
2149
|
if (this.#selectedDates.length === 2 && !isClickInRange) {
|
|
1975
2150
|
this.#clearRangeSelection();
|
|
@@ -1980,7 +2155,8 @@ var RollDate = (function () {
|
|
|
1980
2155
|
$day.classList.add('RollDate__calendar__day--range-first');
|
|
1981
2156
|
} else {
|
|
1982
2157
|
const firstDate = this.#selectedDates[0];
|
|
1983
|
-
|
|
2158
|
+
const firstStamp = this.#toDateStamp(firstDate);
|
|
2159
|
+
if (clickStamp > firstStamp) {
|
|
1984
2160
|
this.#selectedDates = [firstDate, date];
|
|
1985
2161
|
|
|
1986
2162
|
this.$container.querySelectorAll('[data-day]').forEach(dayEl => {
|
|
@@ -1989,17 +2165,17 @@ var RollDate = (function () {
|
|
|
1989
2165
|
Number(dayEl.dataset.month),
|
|
1990
2166
|
Number(dayEl.dataset.day)
|
|
1991
2167
|
);
|
|
1992
|
-
const
|
|
2168
|
+
const stamp = this.#toDateStamp(dayDate);
|
|
1993
2169
|
|
|
1994
|
-
if (
|
|
2170
|
+
if (stamp === firstStamp) {
|
|
1995
2171
|
dayEl.classList.add('RollDate__calendar__day--range-first');
|
|
1996
|
-
} else if (
|
|
2172
|
+
} else if (stamp === clickStamp) {
|
|
1997
2173
|
dayEl.classList.add('RollDate__calendar__day--range-last');
|
|
1998
|
-
} else if (
|
|
2174
|
+
} else if (stamp > firstStamp && stamp < clickStamp) {
|
|
1999
2175
|
dayEl.classList.add('RollDate__calendar__day--range-selected');
|
|
2000
2176
|
}
|
|
2001
2177
|
});
|
|
2002
|
-
} else if (
|
|
2178
|
+
} else if (clickStamp < firstStamp) {
|
|
2003
2179
|
this.#selectedDates = [date];
|
|
2004
2180
|
$day.classList.add('RollDate__calendar__day--range-first');
|
|
2005
2181
|
|
|
@@ -2017,12 +2193,13 @@ var RollDate = (function () {
|
|
|
2017
2193
|
}
|
|
2018
2194
|
|
|
2019
2195
|
if (this.options.selectType === 'multi') {
|
|
2196
|
+
const clickStamp = this.#toDateStamp(date);
|
|
2020
2197
|
const isSelected = this.#selectedDates.some(
|
|
2021
|
-
d => d
|
|
2198
|
+
d => this.#toDateStamp(d) === clickStamp
|
|
2022
2199
|
);
|
|
2023
2200
|
if (isSelected) {
|
|
2024
2201
|
this.#selectedDates = this.#selectedDates.filter(
|
|
2025
|
-
d => d
|
|
2202
|
+
d => this.#toDateStamp(d) !== clickStamp
|
|
2026
2203
|
);
|
|
2027
2204
|
$day.classList.remove('RollDate__calendar__day--selected');
|
|
2028
2205
|
} else {
|
|
@@ -2172,6 +2349,20 @@ var RollDate = (function () {
|
|
|
2172
2349
|
return this.#selectedDates
|
|
2173
2350
|
}
|
|
2174
2351
|
|
|
2352
|
+
/** Visible calendar month (updates while scrolling). Month is 0–11. */
|
|
2353
|
+
getViewMonth() {
|
|
2354
|
+
return {
|
|
2355
|
+
year: this.data.current_year,
|
|
2356
|
+
month: this.data.current_month
|
|
2357
|
+
}
|
|
2358
|
+
}
|
|
2359
|
+
|
|
2360
|
+
/** First day of the month currently shown in the calendar. */
|
|
2361
|
+
getViewDate() {
|
|
2362
|
+
const { year, month } = this.getViewMonth();
|
|
2363
|
+
return new Date(year, month, 1)
|
|
2364
|
+
}
|
|
2365
|
+
|
|
2175
2366
|
setDisabledDates(dates = []) {
|
|
2176
2367
|
this.options.disabledDates = Array.isArray(dates) ? dates : [];
|
|
2177
2368
|
this.#disabledDateStamps = this.#buildDisabledDateSet(this.options.disabledDates);
|
|
@@ -2201,6 +2392,251 @@ var RollDate = (function () {
|
|
|
2201
2392
|
return normalized ? this.#isDateDisabled(normalized) : false
|
|
2202
2393
|
}
|
|
2203
2394
|
|
|
2395
|
+
setHighlightDates(dates = []) {
|
|
2396
|
+
this.options.highlightDates = Array.isArray(dates) ? dates : [];
|
|
2397
|
+
this.#highlightDateMap = this.#buildHighlightDateMap(this.options.highlightDates);
|
|
2398
|
+
this.#updateView(this.#viewNumber);
|
|
2399
|
+
}
|
|
2400
|
+
|
|
2401
|
+
highlightDate(dateLike, color) {
|
|
2402
|
+
const normalized = this.#normalizeDateInput(dateLike);
|
|
2403
|
+
if (!normalized) return
|
|
2404
|
+
|
|
2405
|
+
const stamp = this.#toDateStamp(normalized);
|
|
2406
|
+
const existing = this.#highlightDateMap.get(stamp) || [];
|
|
2407
|
+
const nextColor = color == null || color === ''
|
|
2408
|
+
? null
|
|
2409
|
+
: this.#sanitizeHighlightColor(color);
|
|
2410
|
+
|
|
2411
|
+
if (color != null && color !== '' && nextColor === undefined) return
|
|
2412
|
+
|
|
2413
|
+
existing.push(nextColor ?? null);
|
|
2414
|
+
this.#highlightDateMap.set(stamp, existing);
|
|
2415
|
+
this.options.highlightDates = this.#highlightMapToOptionsArray();
|
|
2416
|
+
this.#updateView(this.#viewNumber);
|
|
2417
|
+
}
|
|
2418
|
+
|
|
2419
|
+
unhighlightDate(dateLike, color) {
|
|
2420
|
+
const normalized = this.#normalizeDateInput(dateLike);
|
|
2421
|
+
if (!normalized) return
|
|
2422
|
+
|
|
2423
|
+
const stamp = this.#toDateStamp(normalized);
|
|
2424
|
+
const existing = this.#highlightDateMap.get(stamp);
|
|
2425
|
+
if (!existing?.length) return
|
|
2426
|
+
|
|
2427
|
+
if (color === undefined) {
|
|
2428
|
+
this.#highlightDateMap.delete(stamp);
|
|
2429
|
+
} else {
|
|
2430
|
+
const target = color == null || color === ''
|
|
2431
|
+
? null
|
|
2432
|
+
: this.#sanitizeHighlightColor(color);
|
|
2433
|
+
|
|
2434
|
+
const index = existing.findIndex(item => item === target);
|
|
2435
|
+
if (index === -1) return
|
|
2436
|
+
|
|
2437
|
+
existing.splice(index, 1);
|
|
2438
|
+
if (existing.length) {
|
|
2439
|
+
this.#highlightDateMap.set(stamp, existing);
|
|
2440
|
+
} else {
|
|
2441
|
+
this.#highlightDateMap.delete(stamp);
|
|
2442
|
+
}
|
|
2443
|
+
}
|
|
2444
|
+
|
|
2445
|
+
this.options.highlightDates = this.#highlightMapToOptionsArray();
|
|
2446
|
+
this.#updateView(this.#viewNumber);
|
|
2447
|
+
}
|
|
2448
|
+
|
|
2449
|
+
isDateHighlighted(dateLike) {
|
|
2450
|
+
const normalized = this.#normalizeDateInput(dateLike);
|
|
2451
|
+
return normalized ? this.#isDateHighlighted(normalized) : false
|
|
2452
|
+
}
|
|
2453
|
+
|
|
2454
|
+
getHighlightColors(dateLike) {
|
|
2455
|
+
const normalized = this.#normalizeDateInput(dateLike);
|
|
2456
|
+
if (!normalized) return []
|
|
2457
|
+
return [...(this.#highlightDateMap.get(this.#toDateStamp(normalized)) || [])]
|
|
2458
|
+
}
|
|
2459
|
+
|
|
2460
|
+
getHighlightColor(dateLike) {
|
|
2461
|
+
return this.getHighlightColors(dateLike).find(color => color != null) ?? null
|
|
2462
|
+
}
|
|
2463
|
+
|
|
2464
|
+
goToDate(dateLike) {
|
|
2465
|
+
const normalized = this.#normalizeDateInput(dateLike);
|
|
2466
|
+
if (!normalized) return false
|
|
2467
|
+
|
|
2468
|
+
const clamped = this.#clampDateToRange(
|
|
2469
|
+
normalized,
|
|
2470
|
+
this.options.minDate,
|
|
2471
|
+
this.options.maxDate
|
|
2472
|
+
);
|
|
2473
|
+
|
|
2474
|
+
this.data.current_year = clamped.getFullYear();
|
|
2475
|
+
this.data.current_month = clamped.getMonth();
|
|
2476
|
+
this.data.current_decade = getDecade(clamped.getFullYear());
|
|
2477
|
+
|
|
2478
|
+
if (this.#viewNumber !== 0) {
|
|
2479
|
+
this.#viewNumber = 0;
|
|
2480
|
+
for (const period of this.#viewPeriodNames) {
|
|
2481
|
+
this.$container.classList.remove('RollDate__calendar__type--' + period + 's');
|
|
2482
|
+
}
|
|
2483
|
+
this.$container.classList.add('RollDate__calendar__type--days');
|
|
2484
|
+
}
|
|
2485
|
+
|
|
2486
|
+
this.#updateView(0);
|
|
2487
|
+
return true
|
|
2488
|
+
}
|
|
2489
|
+
|
|
2490
|
+
getValue() {
|
|
2491
|
+
if (this.options.selectType === 'single') {
|
|
2492
|
+
const date = this.#selectedDates[0];
|
|
2493
|
+
return date ? new Date(date.getTime()) : null
|
|
2494
|
+
}
|
|
2495
|
+
|
|
2496
|
+
return this.#selectedDates.map(date => new Date(date.getTime()))
|
|
2497
|
+
}
|
|
2498
|
+
|
|
2499
|
+
setValue(value) {
|
|
2500
|
+
if (value == null || value === '') {
|
|
2501
|
+
this.clearSelection();
|
|
2502
|
+
return true
|
|
2503
|
+
}
|
|
2504
|
+
|
|
2505
|
+
if (this.options.selectType === 'single') {
|
|
2506
|
+
const normalized = this.#normalizeDateInput(value);
|
|
2507
|
+
if (!normalized || this.#isDateDisabled(normalized)) return false
|
|
2508
|
+
this.#selectedDates = [this.#applyTimeToDate(normalized)];
|
|
2509
|
+
} else if (this.options.selectType === 'range') {
|
|
2510
|
+
const raw = Array.isArray(value) ? value : [value];
|
|
2511
|
+
const parsed = raw
|
|
2512
|
+
.map(item => this.#normalizeDateInput(item))
|
|
2513
|
+
.filter(Boolean)
|
|
2514
|
+
.map(date => this.#clampDateToRange(date, this.options.minDate, this.options.maxDate))
|
|
2515
|
+
.filter(date => !this.#isDateDisabled(date));
|
|
2516
|
+
|
|
2517
|
+
if (!parsed.length) return false
|
|
2518
|
+
|
|
2519
|
+
let start = parsed[0];
|
|
2520
|
+
let end = parsed.length > 1 ? parsed[1] : parsed[0];
|
|
2521
|
+
if (start > end) [start, end] = [end, start];
|
|
2522
|
+
|
|
2523
|
+
this.#selectedDates = parsed.length > 1
|
|
2524
|
+
? [this.#applyTimeToDate(start), this.#applyTimeToDate(end)]
|
|
2525
|
+
: [this.#applyTimeToDate(start)];
|
|
2526
|
+
} else {
|
|
2527
|
+
const raw = Array.isArray(value) ? value : [value];
|
|
2528
|
+
const parsed = raw
|
|
2529
|
+
.map(item => this.#normalizeDateInput(item))
|
|
2530
|
+
.filter(Boolean)
|
|
2531
|
+
.map(date => this.#clampDateToRange(date, this.options.minDate, this.options.maxDate))
|
|
2532
|
+
.filter(date => !this.#isDateDisabled(date))
|
|
2533
|
+
.map(date => this.#applyTimeToDate(date));
|
|
2534
|
+
|
|
2535
|
+
if (!parsed.length) return false
|
|
2536
|
+
|
|
2537
|
+
const unique = [];
|
|
2538
|
+
const seen = new Set();
|
|
2539
|
+
parsed.forEach(date => {
|
|
2540
|
+
const stamp = this.#toDateStamp(date);
|
|
2541
|
+
if (seen.has(stamp)) return
|
|
2542
|
+
seen.add(stamp);
|
|
2543
|
+
unique.push(date);
|
|
2544
|
+
});
|
|
2545
|
+
this.#selectedDates = unique;
|
|
2546
|
+
}
|
|
2547
|
+
|
|
2548
|
+
this.#paintSelection();
|
|
2549
|
+
this.#notifySelectionChange();
|
|
2550
|
+
this.#updateInputValue();
|
|
2551
|
+
return true
|
|
2552
|
+
}
|
|
2553
|
+
|
|
2554
|
+
#dayElementStamp(dayEl) {
|
|
2555
|
+
return this.#toDateStamp(new Date(
|
|
2556
|
+
Number(dayEl.dataset.year),
|
|
2557
|
+
Number(dayEl.dataset.month),
|
|
2558
|
+
Number(dayEl.dataset.day)
|
|
2559
|
+
))
|
|
2560
|
+
}
|
|
2561
|
+
|
|
2562
|
+
#findDayElement(date) {
|
|
2563
|
+
if (!(date instanceof Date)) return null
|
|
2564
|
+
return this.$container.querySelector(
|
|
2565
|
+
`.RollDate__calendar__day[data-year="${date.getFullYear()}"][data-month="${date.getMonth()}"][data-day="${date.getDate()}"]`
|
|
2566
|
+
)
|
|
2567
|
+
}
|
|
2568
|
+
|
|
2569
|
+
#paintRangeSelection() {
|
|
2570
|
+
this.$container.querySelectorAll('[data-day]').forEach(dayEl => {
|
|
2571
|
+
dayEl.classList.remove(
|
|
2572
|
+
'RollDate__calendar__day--range-first',
|
|
2573
|
+
'RollDate__calendar__day--range-last',
|
|
2574
|
+
'RollDate__calendar__day--range-selected'
|
|
2575
|
+
);
|
|
2576
|
+
});
|
|
2577
|
+
|
|
2578
|
+
if (!this.#selectedDates.length) return
|
|
2579
|
+
|
|
2580
|
+
const firstStamp = this.#toDateStamp(this.#selectedDates[0]);
|
|
2581
|
+
|
|
2582
|
+
if (this.#selectedDates.length === 1) {
|
|
2583
|
+
this.#findDayElement(this.#selectedDates[0])
|
|
2584
|
+
?.classList.add('RollDate__calendar__day--range-first');
|
|
2585
|
+
return
|
|
2586
|
+
}
|
|
2587
|
+
|
|
2588
|
+
const endStamp = this.#toDateStamp(this.#selectedDates[1]);
|
|
2589
|
+
|
|
2590
|
+
this.$container.querySelectorAll('[data-day]').forEach(dayEl => {
|
|
2591
|
+
const stamp = this.#dayElementStamp(dayEl);
|
|
2592
|
+
|
|
2593
|
+
if (stamp === firstStamp) {
|
|
2594
|
+
dayEl.classList.add('RollDate__calendar__day--range-first');
|
|
2595
|
+
} else if (stamp === endStamp) {
|
|
2596
|
+
dayEl.classList.add('RollDate__calendar__day--range-last');
|
|
2597
|
+
} else if (stamp > firstStamp && stamp < endStamp) {
|
|
2598
|
+
dayEl.classList.add('RollDate__calendar__day--range-selected');
|
|
2599
|
+
}
|
|
2600
|
+
});
|
|
2601
|
+
}
|
|
2602
|
+
|
|
2603
|
+
#paintSingleSelection() {
|
|
2604
|
+
this.$container.querySelectorAll('[data-day]').forEach(dayEl => {
|
|
2605
|
+
dayEl.classList.remove('RollDate__calendar__day--selected');
|
|
2606
|
+
});
|
|
2607
|
+
|
|
2608
|
+
const selected = this.#selectedDates[0];
|
|
2609
|
+
if (selected) {
|
|
2610
|
+
this.#findDayElement(selected)?.classList.add('RollDate__calendar__day--selected');
|
|
2611
|
+
}
|
|
2612
|
+
}
|
|
2613
|
+
|
|
2614
|
+
#paintMultiSelection() {
|
|
2615
|
+
const selectedStamps = new Set(this.#selectedDates.map(date => this.#toDateStamp(date)));
|
|
2616
|
+
|
|
2617
|
+
this.$container.querySelectorAll('[data-day]').forEach(dayEl => {
|
|
2618
|
+
if (selectedStamps.has(this.#dayElementStamp(dayEl))) {
|
|
2619
|
+
dayEl.classList.add('RollDate__calendar__day--selected');
|
|
2620
|
+
} else {
|
|
2621
|
+
dayEl.classList.remove('RollDate__calendar__day--selected');
|
|
2622
|
+
}
|
|
2623
|
+
});
|
|
2624
|
+
}
|
|
2625
|
+
|
|
2626
|
+
#paintSelection() {
|
|
2627
|
+
if (this.options.selectType === 'range') {
|
|
2628
|
+
this.#paintRangeSelection();
|
|
2629
|
+
return
|
|
2630
|
+
}
|
|
2631
|
+
|
|
2632
|
+
if (this.options.selectType === 'multi') {
|
|
2633
|
+
this.#paintMultiSelection();
|
|
2634
|
+
return
|
|
2635
|
+
}
|
|
2636
|
+
|
|
2637
|
+
this.#paintSingleSelection();
|
|
2638
|
+
}
|
|
2639
|
+
|
|
2204
2640
|
#clearRangeSelection() {
|
|
2205
2641
|
this.#selectedDates = [];
|
|
2206
2642
|
this.$container.querySelectorAll('[data-day]').forEach(item => {
|
|
@@ -2378,7 +2814,8 @@ var RollDate = (function () {
|
|
|
2378
2814
|
} else if (this.options.selectType === 'range') {
|
|
2379
2815
|
this.#selectedDates = [selected];
|
|
2380
2816
|
} else if (this.options.selectType === 'multi') {
|
|
2381
|
-
const
|
|
2817
|
+
const selectedStamp = this.#toDateStamp(selected);
|
|
2818
|
+
const exists = this.#selectedDates.some(d => this.#toDateStamp(d) === selectedStamp);
|
|
2382
2819
|
if (!exists) this.#selectedDates.push(selected);
|
|
2383
2820
|
}
|
|
2384
2821
|
|
|
@@ -2398,9 +2835,10 @@ var RollDate = (function () {
|
|
|
2398
2835
|
this.#clearMultiSelection();
|
|
2399
2836
|
} else {
|
|
2400
2837
|
this.#selectedDates = [];
|
|
2838
|
+
this.$container.querySelectorAll('[data-day].RollDate__calendar__day--selected')
|
|
2839
|
+
.forEach(el => el.classList.remove('RollDate__calendar__day--selected'));
|
|
2401
2840
|
}
|
|
2402
2841
|
|
|
2403
|
-
this.#updateView(this.#viewNumber);
|
|
2404
2842
|
this.#notifySelectionChange();
|
|
2405
2843
|
this.#updateInputValue();
|
|
2406
2844
|
}
|