@@ -23,7 +23,13 @@ import {
2323 isDateInRange ,
2424 isDateSelected ,
2525 isDisableDateInRange ,
26- isToday
26+ isMonthDisabled ,
27+ isMonthInRange ,
28+ isMonthSelected ,
29+ isToday ,
30+ isYearDisabled ,
31+ isYearInRange ,
32+ isYearSelected
2733} from './util/calendar.js'
2834
2935/**
@@ -185,6 +191,13 @@ class Calendar extends BaseComponent {
185191 this . _view = viewMap [ this . _config . selectionType ] || 'days'
186192 }
187193
194+ _classNames ( classNames ) {
195+ return Object . entries ( classNames )
196+ . filter ( ( [ _ , value ] ) => Boolean ( value ) )
197+ . map ( ( [ key ] ) => key )
198+ . join ( ' ' )
199+ }
200+
188201 _getDate ( target ) {
189202 if ( this . _config . selectionType === 'week' ) {
190203 const firstCell = SelectorEngine . findOne ( SELECTOR_CALENDAR_CELL , target . closest ( SELECTOR_CALENDAR_ROW ) )
@@ -200,10 +213,6 @@ class Calendar extends BaseComponent {
200213 const cloneDate = new Date ( date )
201214 const index = Manipulator . getDataAttribute ( target . closest ( SELECTOR_CALENDAR ) , 'calendar-index' )
202215
203- if ( isDateDisabled ( date , this . _config . minDate , this . _config . maxDate , this . _config . disabledDates ) ) {
204- return
205- }
206-
207216 if ( this . _view === 'days' ) {
208217 this . _setCalendarDate ( index ? new Date ( cloneDate . setMonth ( cloneDate . getMonth ( ) - index ) ) : date )
209218 }
@@ -222,6 +231,11 @@ class Calendar extends BaseComponent {
222231 return
223232 }
224233
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+
225239 this . _hoverDate = null
226240 this . _selectDate ( date )
227241 this . _updateClassNamesAndAriaLabels ( )
@@ -630,49 +644,45 @@ class Calendar extends BaseComponent {
630644 `${ calendarDate . getFullYear ( ) } W${ week . weekNumber } ` ,
631645 this . _config . selectionType
632646 )
647+ const rowAttributes = this . _rowWeekAttributes ( date )
633648 return (
634649 `<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"' : '' }
642653 >
643654 ${ this . _config . showWeekNumber ?
644655 `<th class="calendar-cell-week-number">${ week . weekNumber === 0 ? 53 : week . weekNumber } </td>` : ''
645656 }
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>`
664673 )
665674 } ) . join ( '' ) : '' }
666675 ${ this . _view === 'months' ? listOfMonths . map ( ( row , index ) => (
667676 `<tr>
668677 ${ row . map ( ( month , idx ) => {
669678 const date = new Date ( calendarDate . getFullYear ( ) , ( index * 3 ) + idx , 1 )
679+ const cellAttributes = this . _cellMonthAttributes ( date )
670680 return (
671681 `<td
672- class="${ CLASS_NAME_CALENDAR_CELL } ${ this . _sharedClassNames ( date ) } "
682+ class="${ cellAttributes . className } "
683+ tabindex="${ cellAttributes . tabIndex } "
684+ ${ cellAttributes . ariaSelected ? 'aria-selected="true"' : '' }
673685 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"' : '' }
676686 >
677687 <div class="calendar-cell-inner month">
678688 ${ month }
@@ -686,12 +696,13 @@ class Calendar extends BaseComponent {
686696 `<tr>
687697 ${ row . map ( year => {
688698 const date = new Date ( year , 0 , 1 )
699+ const cellAttributes = this . _cellYearAttributes ( date )
689700 return (
690701 `<td
691- class="${ CLASS_NAME_CALENDAR_CELL } ${ this . _sharedClassNames ( date ) } "
702+ class="${ cellAttributes . className } "
703+ tabindex="${ cellAttributes . tabIndex } "
704+ ${ cellAttributes . ariaSelected ? 'aria-selected="true"' : '' }
692705 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"' : '' }
695706 >
696707 <div class="calendar-cell-inner year">
697708 ${ year }
@@ -740,11 +751,12 @@ class Calendar extends BaseComponent {
740751 for ( const row of rows ) {
741752 const firstCell = SelectorEngine . findOne ( SELECTOR_CALENDAR_CELL , row )
742753 const date = new Date ( Manipulator . getDataAttribute ( firstCell , 'date' ) )
743- const classNames = this . _sharedClassNames ( date )
754+ const rowAttributes = this . _rowWeekAttributes ( date )
744755
745- row . className = `${ CLASS_NAME_CALENDAR_ROW } ${ classNames } `
756+ row . className = rowAttributes . className
757+ row . tabIndex = rowAttributes . tabIndex
746758
747- if ( isDateSelected ( date , this . _startDate , this . _endDate ) ) {
759+ if ( rowAttributes . ariaSelected ) {
748760 row . setAttribute ( 'aria-selected' , true )
749761 } else {
750762 row . removeAttribute ( 'aria-selected' )
@@ -758,68 +770,121 @@ class Calendar extends BaseComponent {
758770
759771 for ( const cell of cells ) {
760772 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
762774
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+ }
764782
765- if ( isDateSelected ( date , this . _startDate , this . _endDate ) ) {
783+ cell . className = cellAttributes . className
784+ cell . tabIndex = cellAttributes . tabIndex
785+
786+ if ( cellAttributes . ariaSelected ) {
766787 cell . setAttribute ( 'aria-selected' , true )
767788 } else {
768789 cell . removeAttribute ( 'aria-selected' )
769790 }
770791 }
771792 }
772793
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 ,
775801 ...( 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 &&
780806 ( this . _hoverDate && this . _selectEndDate ?
781807 isDateInRange ( date , this . _startDate , this . _hoverDate ) :
782808 isDateInRange ( date , this . _hoverDate , this . _endDate ) ) ,
783- selected : isDateSelected ( date , this . _startDate , this . _endDate )
809+ selected : isSelected
784810 } ) ,
785811 today : isToday ( date ) ,
786812 [ 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
787821 }
822+ }
788823
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+ } )
790838
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
795843 }
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+ } )
796860
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
812865 }
866+ }
813867
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+ } )
815882
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
820887 }
821-
822- return Object . keys ( result ) . join ( ' ' )
823888 }
824889
825890 // Static
0 commit comments