@@ -23,7 +23,13 @@ import {
23
23
isDateInRange ,
24
24
isDateSelected ,
25
25
isDisableDateInRange ,
26
- isToday
26
+ isMonthDisabled ,
27
+ isMonthInRange ,
28
+ isMonthSelected ,
29
+ isToday ,
30
+ isYearDisabled ,
31
+ isYearInRange ,
32
+ isYearSelected
27
33
} from './util/calendar.js'
28
34
29
35
/**
@@ -185,6 +191,13 @@ class Calendar extends BaseComponent {
185
191
this . _view = viewMap [ this . _config . selectionType ] || 'days'
186
192
}
187
193
194
+ _classNames ( classNames ) {
195
+ return Object . entries ( classNames )
196
+ . filter ( ( [ _ , value ] ) => Boolean ( value ) )
197
+ . map ( ( [ key ] ) => key )
198
+ . join ( ' ' )
199
+ }
200
+
188
201
_getDate ( target ) {
189
202
if ( this . _config . selectionType === 'week' ) {
190
203
const firstCell = SelectorEngine . findOne ( SELECTOR_CALENDAR_CELL , target . closest ( SELECTOR_CALENDAR_ROW ) )
@@ -200,10 +213,6 @@ class Calendar extends BaseComponent {
200
213
const cloneDate = new Date ( date )
201
214
const index = Manipulator . getDataAttribute ( target . closest ( SELECTOR_CALENDAR ) , 'calendar-index' )
202
215
203
- if ( isDateDisabled ( date , this . _config . minDate , this . _config . maxDate , this . _config . disabledDates ) ) {
204
- return
205
- }
206
-
207
216
if ( this . _view === 'days' ) {
208
217
this . _setCalendarDate ( index ? new Date ( cloneDate . setMonth ( cloneDate . getMonth ( ) - index ) ) : date )
209
218
}
@@ -222,6 +231,11 @@ class Calendar extends BaseComponent {
222
231
return
223
232
}
224
233
234
+ // Allow to change the calendarDate but not startDate or endDate
235
+ if ( isDateDisabled ( date , this . _config . minDate , this . _config . maxDate , this . _config . disabledDates ) ) {
236
+ return
237
+ }
238
+
225
239
this . _hoverDate = null
226
240
this . _selectDate ( date )
227
241
this . _updateClassNamesAndAriaLabels ( )
@@ -630,49 +644,45 @@ class Calendar extends BaseComponent {
630
644
`${ calendarDate . getFullYear ( ) } W${ week . weekNumber } ` ,
631
645
this . _config . selectionType
632
646
)
647
+ const rowAttributes = this . _rowWeekAttributes ( date )
633
648
return (
634
649
`<tr
635
- class="${ CLASS_NAME_CALENDAR_ROW } ${ this . _config . selectionType === 'week' && this . _sharedClassNames ( date ) } "
636
- tabindex="${
637
- this . _config . selectionType === 'week' &&
638
- week . days . some ( day => day . month === 'current' ) &&
639
- ! isDateDisabled ( date , this . _config . minDate , this . _config . maxDate , this . _config . disabledDates ) ? 0 : - 1
640
- } "
641
- ${ isDateSelected ( date , this . _startDate , this . _endDate ) ? 'aria-selected="true"' : '' }
650
+ class="${ rowAttributes . className } "
651
+ tabindex="${ rowAttributes . tabIndex } "
652
+ ${ rowAttributes . ariaSelected ? 'aria-selected="true"' : '' }
642
653
>
643
654
${ this . _config . showWeekNumber ?
644
655
`<th class="calendar-cell-week-number">${ week . weekNumber === 0 ? 53 : week . weekNumber } </td>` : ''
645
656
}
646
- ${ week . days . map ( ( { date, month } ) => (
647
- month === 'current' || this . _config . showAdjacementDays ?
648
- `<td
649
- class="${ CLASS_NAME_CALENDAR_CELL } ${ this . _dayClassNames ( date , month ) } "
650
- tabindex="${
651
- this . _config . selectionType === 'day' &&
652
- ( month === 'current' || this . _config . selectAdjacementDays ) &&
653
- ! isDateDisabled ( date , this . _config . minDate , this . _config . maxDate , this . _config . disabledDates ) ? 0 : - 1
654
- } "
655
- ${ isDateSelected ( date , this . _startDate , this . _endDate ) ? 'aria-selected="true"' : '' }
656
- data-coreui-date="${ date } "
657
- >
658
- <div class="calendar-cell-inner day">
659
- ${ date . toLocaleDateString ( this . _config . locale , { day : 'numeric' } ) }
660
- </div>
661
- </td>` :
662
- '<td></td>'
663
- ) ) . join ( '' ) } </tr>`
657
+ ${ week . days . map ( ( { date, month } ) => {
658
+ const cellAttributes = this . _cellDayAttributes ( date , month )
659
+ return month === 'current' || this . _config . showAdjacementDays ?
660
+ `<td
661
+ class="${ cellAttributes . className } "
662
+ tabindex="${ cellAttributes . tabIndex } "
663
+ ${ cellAttributes . ariaSelected ? 'aria-selected="true"' : '' }
664
+ data-coreui-date="${ date } "
665
+ >
666
+ <div class="calendar-cell-inner day">
667
+ ${ date . toLocaleDateString ( this . _config . locale , { day : 'numeric' } ) }
668
+ </div>
669
+ </td>` :
670
+ '<td></td>'
671
+ }
672
+ ) . join ( '' ) } </tr>`
664
673
)
665
674
} ) . join ( '' ) : '' }
666
675
${ this . _view === 'months' ? listOfMonths . map ( ( row , index ) => (
667
676
`<tr>
668
677
${ row . map ( ( month , idx ) => {
669
678
const date = new Date ( calendarDate . getFullYear ( ) , ( index * 3 ) + idx , 1 )
679
+ const cellAttributes = this . _cellMonthAttributes ( date )
670
680
return (
671
681
`<td
672
- class="${ CLASS_NAME_CALENDAR_CELL } ${ this . _sharedClassNames ( date ) } "
682
+ class="${ cellAttributes . className } "
683
+ tabindex="${ cellAttributes . tabIndex } "
684
+ ${ cellAttributes . ariaSelected ? 'aria-selected="true"' : '' }
673
685
data-coreui-date="${ date . toDateString ( ) } "
674
- tabindex="${ isDateDisabled ( date , this . _config . minDate , this . _config . maxDate , this . _config . disabledDates ) ? - 1 : 0 } "
675
- ${ isDateSelected ( date , this . _startDate , this . _endDate ) ? 'aria-selected="true"' : '' }
676
686
>
677
687
<div class="calendar-cell-inner month">
678
688
${ month }
@@ -686,12 +696,13 @@ class Calendar extends BaseComponent {
686
696
`<tr>
687
697
${ row . map ( year => {
688
698
const date = new Date ( year , 0 , 1 )
699
+ const cellAttributes = this . _cellYearAttributes ( date )
689
700
return (
690
701
`<td
691
- class="${ CLASS_NAME_CALENDAR_CELL } ${ this . _sharedClassNames ( date ) } "
702
+ class="${ cellAttributes . className } "
703
+ tabindex="${ cellAttributes . tabIndex } "
704
+ ${ cellAttributes . ariaSelected ? 'aria-selected="true"' : '' }
692
705
data-coreui-date="${ date . toDateString ( ) } "
693
- tabindex="${ isDateDisabled ( date , this . _config . minDate , this . _config . maxDate , this . _config . disabledDates ) ? - 1 : 0 } "
694
- ${ isDateSelected ( date , this . _startDate , this . _endDate ) ? 'aria-selected="true"' : '' }
695
706
>
696
707
<div class="calendar-cell-inner year">
697
708
${ year }
@@ -740,11 +751,12 @@ class Calendar extends BaseComponent {
740
751
for ( const row of rows ) {
741
752
const firstCell = SelectorEngine . findOne ( SELECTOR_CALENDAR_CELL , row )
742
753
const date = new Date ( Manipulator . getDataAttribute ( firstCell , 'date' ) )
743
- const classNames = this . _sharedClassNames ( date )
754
+ const rowAttributes = this . _rowWeekAttributes ( date )
744
755
745
- row . className = `${ CLASS_NAME_CALENDAR_ROW } ${ classNames } `
756
+ row . className = rowAttributes . className
757
+ row . tabIndex = rowAttributes . tabIndex
746
758
747
- if ( isDateSelected ( date , this . _startDate , this . _endDate ) ) {
759
+ if ( rowAttributes . ariaSelected ) {
748
760
row . setAttribute ( 'aria-selected' , true )
749
761
} else {
750
762
row . removeAttribute ( 'aria-selected' )
@@ -758,68 +770,121 @@ class Calendar extends BaseComponent {
758
770
759
771
for ( const cell of cells ) {
760
772
const date = new Date ( Manipulator . getDataAttribute ( cell , 'date' ) )
761
- const classNames = this . _config . selectionType === 'day' ? this . _dayClassNames ( date , 'current' ) : this . _sharedClassNames ( date )
773
+ let cellAttributes
762
774
763
- cell . className = `${ CLASS_NAME_CALENDAR_CELL } ${ classNames } `
775
+ if ( this . _view === 'days' ) {
776
+ cellAttributes = this . _cellDayAttributes ( date , 'current' )
777
+ } else if ( this . _view === 'months' ) {
778
+ cellAttributes = this . _cellMonthAttributes ( date )
779
+ } else {
780
+ cellAttributes = this . _cellYearAttributes ( date )
781
+ }
764
782
765
- if ( isDateSelected ( date , this . _startDate , this . _endDate ) ) {
783
+ cell . className = cellAttributes . className
784
+ cell . tabIndex = cellAttributes . tabIndex
785
+
786
+ if ( cellAttributes . ariaSelected ) {
766
787
cell . setAttribute ( 'aria-selected' , true )
767
788
} else {
768
789
cell . removeAttribute ( 'aria-selected' )
769
790
}
770
791
}
771
792
}
772
793
773
- _dayClassNames ( date , month ) {
774
- const classNames = {
794
+ _cellDayAttributes ( date , month ) {
795
+ const isCurrentMonth = month === 'current'
796
+ const isDisabled = isDateDisabled ( date , this . _config . minDate , this . _config . maxDate , this . _config . disabledDates )
797
+ const isSelected = isDateSelected ( date , this . _startDate , this . _endDate )
798
+
799
+ const classNames = this . _classNames ( {
800
+ [ CLASS_NAME_CALENDAR_CELL ] : true ,
775
801
...( this . _config . selectionType === 'day' && this . _view === 'days' && {
776
- clickable : month !== 'current' && this . _config . selectAdjacementDays ,
777
- disabled : isDateDisabled ( date , this . _config . minDate , this . _config . maxDate , this . _config . disabledDates ) ,
778
- range : month === 'current' && isDateInRange ( date , this . _startDate , this . _endDate ) ,
779
- 'range-hover' : month === 'current' &&
802
+ clickable : ! isCurrentMonth && this . _config . selectAdjacementDays ,
803
+ disabled : isDisabled ,
804
+ range : isCurrentMonth && isDateInRange ( date , this . _startDate , this . _endDate ) ,
805
+ 'range-hover' : isCurrentMonth &&
780
806
( this . _hoverDate && this . _selectEndDate ?
781
807
isDateInRange ( date , this . _startDate , this . _hoverDate ) :
782
808
isDateInRange ( date , this . _hoverDate , this . _endDate ) ) ,
783
- selected : isDateSelected ( date , this . _startDate , this . _endDate )
809
+ selected : isSelected
784
810
} ) ,
785
811
today : isToday ( date ) ,
786
812
[ month ] : true
813
+ } )
814
+
815
+ return {
816
+ className : classNames ,
817
+ tabIndex : this . _config . selectionType === 'day' &&
818
+ ( isCurrentMonth || this . _config . selectAdjacementDays ) &&
819
+ ! isDisabled ? 0 : - 1 ,
820
+ ariaSelected : isSelected
787
821
}
822
+ }
788
823
789
- const result = { }
824
+ _cellMonthAttributes ( date ) {
825
+ const isDisabled = isMonthDisabled ( date , this . _config . minDate , this . _config . maxDate , this . _config . disabledDates )
826
+ const isSelected = isMonthSelected ( date , this . _startDate , this . _endDate )
827
+
828
+ const classNames = this . _classNames ( {
829
+ [ CLASS_NAME_CALENDAR_CELL ] : true ,
830
+ disabled : isDisabled ,
831
+ 'range-hover' : this . _config . selectionType === 'month' &&
832
+ ( this . _hoverDate && this . _selectEndDate ?
833
+ isMonthInRange ( date , this . _startDate , this . _hoverDate ) :
834
+ isMonthInRange ( date , this . _hoverDate , this . _endDate ) ) ,
835
+ range : isMonthInRange ( date , this . _startDate , this . _endDate ) ,
836
+ selected : isSelected
837
+ } )
790
838
791
- for ( const key in classNames ) {
792
- if ( classNames [ key ] === true ) {
793
- result [ key ] = true
794
- }
839
+ return {
840
+ className : classNames ,
841
+ tabIndex : isDisabled ? - 1 : 0 ,
842
+ ariaSelected : isSelected
795
843
}
844
+ }
845
+
846
+ _cellYearAttributes ( date ) {
847
+ const isDisabled = isYearDisabled ( date , this . _config . minDate , this . _config . maxDate , this . _config . disabledDates )
848
+ const isSelected = isYearSelected ( date , this . _startDate , this . _endDate )
849
+
850
+ const classNames = this . _classNames ( {
851
+ [ CLASS_NAME_CALENDAR_CELL ] : true ,
852
+ disabled : isDisabled ,
853
+ 'range-hover' : this . _config . selectionType === 'year' &&
854
+ ( this . _hoverDate && this . _selectEndDate ?
855
+ isYearInRange ( date , this . _startDate , this . _hoverDate ) :
856
+ isYearInRange ( date , this . _hoverDate , this . _endDate ) ) ,
857
+ range : isYearInRange ( date , this . _startDate , this . _endDate ) ,
858
+ selected : isSelected
859
+ } )
796
860
797
- return Object . keys ( result ) . join ( ' ' )
798
- }
799
-
800
- _sharedClassNames ( date ) {
801
- const classNames = {
802
- disabled : isDateDisabled ( date , this . _config . minDate , this . _config . maxDate , this . _config . disabledDates ) ,
803
- range : isDateInRange ( date , this . _startDate , this . _endDate ) ,
804
- 'range-hover' : (
805
- ( this . _config . selectionType === 'week' && this . _view === 'days' ) ||
806
- ( this . _config . selectionType === 'month' && this . _view === 'months' ) ||
807
- ( this . _config . selectionType === 'year' && this . _view === 'years' )
808
- ) && ( this . _hoverDate && this . _selectEndDate ?
809
- isDateInRange ( date , this . _startDate , this . _hoverDate ) :
810
- isDateInRange ( date , this . _hoverDate , this . _endDate ) ) ,
811
- selected : isDateSelected ( date , this . _startDate , this . _endDate )
861
+ return {
862
+ className : classNames ,
863
+ tabIndex : isDisabled ? - 1 : 0 ,
864
+ ariaSelected : isSelected
812
865
}
866
+ }
813
867
814
- const result = { }
868
+ _rowWeekAttributes ( date ) {
869
+ const isDisabled = isDateDisabled ( date , this . _config . minDate , this . _config . maxDate , this . _config . disabledDates )
870
+ const isSelected = isDateSelected ( date , this . _startDate , this . _endDate )
871
+
872
+ const classNames = this . _classNames ( {
873
+ [ CLASS_NAME_CALENDAR_ROW ] : true ,
874
+ disabled : isDisabled ,
875
+ range : this . _config . selectionType === 'week' && isDateInRange ( date , this . _startDate , this . _endDate ) ,
876
+ 'range-hover' : this . _config . selectionType === 'week' &&
877
+ ( this . _hoverDate && this . _selectEndDate ?
878
+ isYearInRange ( date , this . _startDate , this . _hoverDate ) :
879
+ isYearInRange ( date , this . _hoverDate , this . _endDate ) ) ,
880
+ selected : isSelected
881
+ } )
815
882
816
- for ( const key in classNames ) {
817
- if ( classNames [ key ] === true ) {
818
- result [ key ] = true
819
- }
883
+ return {
884
+ className : classNames ,
885
+ tabIndex : this . _config . selectionType === 'week' && ! isDisabled ? 0 : - 1 ,
886
+ ariaSelected : isSelected
820
887
}
821
-
822
- return Object . keys ( result ) . join ( ' ' )
823
888
}
824
889
825
890
// Static
0 commit comments