@@ -305,6 +305,10 @@ function getIsSingleValue<T>(value: T | T[]): value is T {
305305 return value && ( ! Array . isArray ( value ) || value . length === 1 ) ;
306306}
307307
308+ function areDatesEqual ( date1 ?: Date | null , date2 ?: Date | null ) {
309+ return date1 instanceof Date && date2 instanceof Date && date1 . getTime ( ) === date2 . getTime ( ) ;
310+ }
311+
308312const isActiveStartDate = PropTypes . instanceOf ( Date ) ;
309313
310314const isValue = PropTypes . oneOfType ( [ PropTypes . string , PropTypes . instanceOf ( Date ) ] ) ;
@@ -507,100 +511,26 @@ export default class Calendar extends Component<CalendarProps, CalendarState> {
507511 } ) ;
508512 }
509513
510- setStateAndCallCallbacks = (
511- action : Action ,
512- nextState : {
513- activeStartDate : Date | null ;
514- value ?: Value ;
515- view ?: View ;
516- } ,
517- event ?: React . MouseEvent < HTMLButtonElement > | undefined ,
518- callback ?: ( { action, activeStartDate, value, view } : OnArgs ) => void ,
519- ) => {
520- const { activeStartDate : previousActiveStartDate , view : previousView } = this ;
521-
522- const { allowPartialRange, onActiveStartDateChange, onChange, onViewChange, selectRange } = this
523- . props as CalendarPropsWithDefaults ;
524-
525- const prevArgs = {
526- activeStartDate : previousActiveStartDate ,
527- value : undefined ,
528- view : previousView ,
529- } ;
530-
531- this . setState ( nextState , ( ) => {
532- const args = {
533- action,
534- activeStartDate : nextState . activeStartDate || this . activeStartDate ,
535- value : ( 'value' in nextState && nextState . value ) || this . value ,
536- view : ( 'view' in nextState && nextState . view ) || this . view ,
537- } ;
538-
539- function shouldUpdate ( key : 'activeStartDate' | 'value' | 'view' ) {
540- // Key must exist, and…
541- if ( ! ( key in nextState ) ) {
542- return false ;
543- }
544-
545- const nextValue = nextState [ key ] ;
546- const prevValue = prevArgs [ key ] ;
547-
548- // …key changed from defined to undefined or the other way around, or…
549- if ( typeof nextValue !== typeof prevValue ) {
550- return true ;
551- }
552-
553- // …value changed.
554- if ( nextValue instanceof Date && prevValue instanceof Date ) {
555- return nextValue . getTime ( ) !== prevValue . getTime ( ) ;
556- } else {
557- return nextValue !== prevValue ;
558- }
559- }
560-
561- if ( shouldUpdate ( 'activeStartDate' ) ) {
562- if ( onActiveStartDateChange ) onActiveStartDateChange ( args ) ;
563- }
564-
565- if ( shouldUpdate ( 'value' ) ) {
566- if ( onChange ) {
567- if ( ! event ) {
568- throw new Error ( 'event is required' ) ;
569- }
570-
571- if ( selectRange ) {
572- const isSingleValue = getIsSingleValue ( nextState . value ) ;
573-
574- if ( ! isSingleValue ) {
575- onChange ( nextState . value || null , event ) ;
576- } else if ( allowPartialRange ) {
577- if ( Array . isArray ( nextState . value ) ) {
578- throw new Error ( 'value must not be an array' ) ;
579- }
580-
581- onChange ( [ nextState . value || null , null ] , event ) ;
582- }
583- } else {
584- onChange ( nextState . value || null , event ) ;
585- }
586- }
587- }
588-
589- if ( shouldUpdate ( 'view' ) ) {
590- if ( onViewChange ) onViewChange ( args ) ;
591- }
592-
593- if ( callback ) callback ( args ) ;
594- } ) ;
595- } ;
596-
597514 /**
598515 * Called when the user uses navigation buttons.
599516 */
600517 setActiveStartDate = ( nextActiveStartDate : Date , action : Action ) => {
601- this . setStateAndCallCallbacks ( action , {
518+ const { onActiveStartDateChange } = this . props as CalendarPropsWithDefaults ;
519+
520+ this . setState ( {
602521 activeStartDate : nextActiveStartDate ,
603522 } ) ;
523+
524+ const args : OnArgs = {
525+ action,
526+ activeStartDate : nextActiveStartDate ,
527+ value : this . value ,
528+ view : this . view ,
529+ } ;
530+
531+ if ( onActiveStartDateChange && ! areDatesEqual ( this . activeStartDate , nextActiveStartDate ) ) {
532+ onActiveStartDateChange ( args ) ;
533+ }
604534 } ;
605535
606536 onClickTile = ( value : Date , event : React . MouseEvent < HTMLButtonElement > ) => {
@@ -634,23 +564,38 @@ export default class Calendar extends Component<CalendarProps, CalendarState> {
634564 this . onClickTile ( nextActiveStartDate , event ) ;
635565
636566 const { view, views } = this ;
637- const { onDrillDown } = this . props as CalendarPropsWithDefaults ;
567+ const { onActiveStartDateChange, onDrillDown, onViewChange } = this
568+ . props as CalendarPropsWithDefaults ;
638569
639570 const nextView = views [ views . indexOf ( view ) + 1 ] ;
640571
641572 if ( ! nextView ) {
642573 throw new Error ( 'Attempted to drill down from the lowest view.' ) ;
643574 }
644575
645- this . setStateAndCallCallbacks (
646- 'drillDown' ,
647- {
648- activeStartDate : nextActiveStartDate ,
649- view : nextView ,
650- } ,
651- undefined ,
652- onDrillDown ,
653- ) ;
576+ this . setState ( {
577+ activeStartDate : nextActiveStartDate ,
578+ view : nextView ,
579+ } ) ;
580+
581+ const args : OnArgs = {
582+ action : 'drillDown' ,
583+ activeStartDate : nextActiveStartDate ,
584+ value : this . value ,
585+ view : nextView ,
586+ } ;
587+
588+ if ( onActiveStartDateChange && ! areDatesEqual ( this . activeStartDate , nextActiveStartDate ) ) {
589+ onActiveStartDateChange ( args ) ;
590+ }
591+
592+ if ( onViewChange && view !== nextView ) {
593+ onViewChange ( args ) ;
594+ }
595+
596+ if ( onDrillDown ) {
597+ onDrillDown ( args ) ;
598+ }
654599 } ;
655600
656601 drillUp = ( ) => {
@@ -659,7 +604,8 @@ export default class Calendar extends Component<CalendarProps, CalendarState> {
659604 }
660605
661606 const { activeStartDate, view, views } = this ;
662- const { onDrillUp } = this . props as CalendarPropsWithDefaults ;
607+ const { onActiveStartDateChange, onDrillUp, onViewChange } = this
608+ . props as CalendarPropsWithDefaults ;
663609
664610 const nextView = views [ views . indexOf ( view ) - 1 ] ;
665611
@@ -669,21 +615,45 @@ export default class Calendar extends Component<CalendarProps, CalendarState> {
669615
670616 const nextActiveStartDate = getBegin ( nextView , activeStartDate ) ;
671617
672- this . setStateAndCallCallbacks (
673- 'drillUp' ,
674- {
675- activeStartDate : nextActiveStartDate ,
676- view : nextView ,
677- } ,
678- undefined ,
679- onDrillUp ,
680- ) ;
618+ this . setState ( {
619+ activeStartDate : nextActiveStartDate ,
620+ view : nextView ,
621+ } ) ;
622+
623+ const args : OnArgs = {
624+ action : 'drillUp' ,
625+ activeStartDate : nextActiveStartDate ,
626+ value : this . value ,
627+ view : nextView ,
628+ } ;
629+
630+ if ( onActiveStartDateChange && ! areDatesEqual ( activeStartDate , nextActiveStartDate ) ) {
631+ onActiveStartDateChange ( args ) ;
632+ }
633+
634+ if ( onViewChange && view !== nextView ) {
635+ onViewChange ( args ) ;
636+ }
637+
638+ if ( onDrillUp ) {
639+ onDrillUp ( args ) ;
640+ }
681641 } ;
682642
683643 onChange = ( value : Date , event : React . MouseEvent < HTMLButtonElement > ) => {
684644 const { value : previousValue } = this ;
685- const { goToRangeStartOnSelect, maxDate, maxDetail, minDate, minDetail, selectRange, view } =
686- this . props as CalendarPropsWithDefaults ;
645+ const {
646+ allowPartialRange,
647+ goToRangeStartOnSelect,
648+ maxDate,
649+ maxDetail,
650+ minDate,
651+ minDetail,
652+ onActiveStartDateChange,
653+ onChange,
654+ selectRange,
655+ view,
656+ } = this . props as CalendarPropsWithDefaults ;
687657
688658 this . onClickTile ( value , event ) ;
689659
@@ -734,14 +704,43 @@ export default class Calendar extends Component<CalendarProps, CalendarState> {
734704
735705 event . persist ( ) ;
736706
737- this . setStateAndCallCallbacks (
738- 'onChange' ,
739- {
740- activeStartDate : nextActiveStartDate ,
741- value : nextValue ,
742- } ,
743- event ,
744- ) ;
707+ this . setState ( {
708+ activeStartDate : nextActiveStartDate ,
709+ value : nextValue ,
710+ } ) ;
711+
712+ const args : OnArgs = {
713+ action : 'onChange' ,
714+ activeStartDate : nextActiveStartDate ,
715+ value : nextValue ,
716+ view : this . view ,
717+ } ;
718+
719+ if ( onActiveStartDateChange && ! areDatesEqual ( this . activeStartDate , nextActiveStartDate ) ) {
720+ onActiveStartDateChange ( args ) ;
721+ }
722+
723+ if ( onChange ) {
724+ if ( ! event ) {
725+ throw new Error ( 'event is required' ) ;
726+ }
727+
728+ if ( selectRange ) {
729+ const isSingleValue = getIsSingleValue ( nextValue ) ;
730+
731+ if ( ! isSingleValue ) {
732+ onChange ( nextValue || null , event ) ;
733+ } else if ( allowPartialRange ) {
734+ if ( Array . isArray ( nextValue ) ) {
735+ throw new Error ( 'value must not be an array' ) ;
736+ }
737+
738+ onChange ( [ nextValue || null , null ] , event ) ;
739+ }
740+ } else {
741+ onChange ( nextValue || null , event ) ;
742+ }
743+ }
745744 } ;
746745
747746 onMouseOver = ( value : Date ) => {
0 commit comments