@@ -620,6 +620,15 @@ axes.calcTicks = function calcTicks(ax, opts) {
620620
621621 generateTicks ( ) ;
622622
623+ var isPeriod = ax . ticklabelmode === 'period' ;
624+ if ( isPeriod ) {
625+ // add one label to show pre tick0 period
626+ tickVals . unshift ( {
627+ minor : false ,
628+ value : axes . tickIncrement ( tickVals [ 0 ] . value , ax . dtick , ! axrev , ax . caldendar )
629+ } ) ;
630+ }
631+
623632 if ( ax . rangebreaks ) {
624633 // replace ticks inside breaks that would get a tick
625634 // and reduce ticks
@@ -681,6 +690,32 @@ axes.calcTicks = function calcTicks(ax, opts) {
681690 ax . _prevDateHead = '' ;
682691 ax . _inCalcTicks = true ;
683692
693+ var minRange = Math . min ( rng [ 0 ] , rng [ 1 ] ) ;
694+ var maxRange = Math . max ( rng [ 0 ] , rng [ 1 ] ) ;
695+
696+ var definedDelta ;
697+ if ( isPeriod && ax . tickformat ) {
698+ var _has = function ( str ) {
699+ return ax . tickformat . indexOf ( str ) !== - 1 ;
700+ } ;
701+
702+ if (
703+ ! _has ( '%f' ) &&
704+ ! _has ( '%H' ) &&
705+ ! _has ( '%I' ) &&
706+ ! _has ( '%L' ) &&
707+ ! _has ( '%Q' ) &&
708+ ! _has ( '%S' ) &&
709+ ! _has ( '%s' ) &&
710+ ! _has ( '%X' )
711+ ) {
712+ if ( _has ( '%x' ) || _has ( '%d' ) || _has ( '%e' ) || _has ( '%j' ) ) definedDelta = ONEDAY ;
713+ else if ( _has ( '%B' ) || _has ( '%b' ) || _has ( '%m' ) ) definedDelta = ONEAVGMONTH ;
714+ else if ( _has ( '%Y' ) || _has ( '%y' ) ) definedDelta = ONEAVGYEAR ;
715+ }
716+ }
717+
718+ var removedPreTick0Label = false ;
684719 var ticksOut = new Array ( tickVals . length ) ;
685720 for ( var i = 0 ; i < tickVals . length ; i ++ ) {
686721 var _minor = tickVals [ i ] . minor ;
@@ -692,6 +727,46 @@ axes.calcTicks = function calcTicks(ax, opts) {
692727 false , // hover
693728 _minor // noSuffixPrefix
694729 ) ;
730+
731+ if ( isPeriod ) {
732+ var v = tickVals [ i ] . value ;
733+
734+ var a = i ;
735+ var b = i + 1 ;
736+ if ( i < tickVals . length - 1 ) {
737+ a = i ;
738+ b = i + 1 ;
739+ } else {
740+ a = i - 1 ;
741+ b = i ;
742+ }
743+
744+ var A = tickVals [ a ] . value ;
745+ var B = tickVals [ b ] . value ;
746+
747+ var delta = definedDelta || Math . abs ( B - A ) ;
748+ var half = axrev ? - 0.5 : 0.5 ;
749+ if ( delta >= ONEDAY * 365 ) { // Years could have days less than ONEAVGYEAR period
750+ v += half * ONEAVGYEAR ;
751+ } else if ( delta >= ONEDAY * 28 ) { // Months could have days less than ONEAVGMONTH period
752+ v += half * ONEAVGMONTH ;
753+ } else if ( delta >= ONEDAY ) {
754+ v += half * ONEDAY ;
755+ }
756+
757+ ticksOut [ i ] . periodX = v ;
758+
759+ if ( v > maxRange || v < minRange ) { // hide label if outside the range
760+ ticksOut [ i ] . text = '' ;
761+ if ( i === 0 ) removedPreTick0Label = true ;
762+ }
763+ }
764+ }
765+
766+ if ( removedPreTick0Label && ticksOut . length > 1 ) {
767+ // redo tick0 text
768+ ax . _prevDateHead = '' ;
769+ ticksOut [ 1 ] . text = axes . tickText ( ax , tickVals [ 1 ] . value ) . text ;
695770 }
696771
697772 ax . _inCalcTicks = false ;
@@ -1785,6 +1860,10 @@ axes.drawOne = function(gd, ax, opts) {
17851860 if ( ! ax . visible ) return ;
17861861
17871862 var transFn = axes . makeTransFn ( ax ) ;
1863+ var transTickLabelFn = ax . ticklabelmode === 'period' ?
1864+ axes . makeTransPeriodFn ( ax ) :
1865+ axes . makeTransFn ( ax ) ;
1866+
17881867 var tickVals ;
17891868 // We remove zero lines, grid lines, and inside ticks if they're within 1px of the end
17901869 // The key case here is removing zero lines when the axis bound is zero
@@ -1903,7 +1982,7 @@ axes.drawOne = function(gd, ax, opts) {
19031982 return axes . drawLabels ( gd , ax , {
19041983 vals : vals ,
19051984 layer : mainAxLayer ,
1906- transFn : transFn ,
1985+ transFn : transTickLabelFn ,
19071986 labelFns : axes . makeLabelFns ( ax , mainLinePosition )
19081987 } ) ;
19091988 } ) ;
@@ -2213,6 +2292,14 @@ axes.makeTransFn = function(ax) {
22132292 function ( d ) { return 'translate(0,' + ( offset + ax . l2p ( d . x ) ) + ')' ; } ;
22142293} ;
22152294
2295+ axes . makeTransPeriodFn = function ( ax ) {
2296+ var axLetter = ax . _id . charAt ( 0 ) ;
2297+ var offset = ax . _offset ;
2298+ return axLetter === 'x' ?
2299+ function ( d ) { return 'translate(' + ( offset + ax . l2p ( d . periodX ) ) + ',0)' ; } :
2300+ function ( d ) { return 'translate(0,' + ( offset + ax . l2p ( d . periodX ) ) + ')' ; } ;
2301+ } ;
2302+
22162303/**
22172304 * Make axis tick path string
22182305 *
@@ -2511,6 +2598,7 @@ axes.drawLabels = function(gd, ax, opts) {
25112598 var axLetter = axId . charAt ( 0 ) ;
25122599 var cls = opts . cls || axId + 'tick' ;
25132600 var vals = opts . vals ;
2601+
25142602 var labelFns = opts . labelFns ;
25152603 var tickAngle = opts . secondary ? 0 : ax . tickangle ;
25162604 var prevAngle = ( ax . _prevTickAngles || { } ) [ cls ] ;
0 commit comments