@@ -31,6 +31,8 @@ var HALFDAY = ONEDAY / 2;
31
31
var ONEHOUR = constants . ONEHOUR ;
32
32
var ONEMIN = constants . ONEMIN ;
33
33
var ONESEC = constants . ONESEC ;
34
+ var ONEMILLI = constants . ONEMILLI ;
35
+ var ONEMICROSEC = constants . ONEMICROSEC ;
34
36
var MINUS_SIGN = constants . MINUS_SIGN ;
35
37
var BADNUM = constants . BADNUM ;
36
38
@@ -908,7 +910,9 @@ axes.calcTicks = function calcTicks(ax, opts) {
908
910
var calendar = ax . calendar ;
909
911
var ticklabelstep = ax . ticklabelstep ;
910
912
var isPeriod = ax . ticklabelmode === 'period' ;
911
-
913
+ var isReversed = ax . range [ 0 ] > ax . range [ 1 ] ;
914
+ var ticklabelIndex = ( ! ax . ticklabelindex || Lib . isArrayOrTypedArray ( ax . ticklabelindex ) ) ?
915
+ ax . ticklabelindex : [ ax . ticklabelindex ] ;
912
916
var rng = Lib . simpleMap ( ax . range , ax . r2l , undefined , undefined , opts ) ;
913
917
var axrev = ( rng [ 1 ] < rng [ 0 ] ) ;
914
918
var minRange = Math . min ( rng [ 0 ] , rng [ 1 ] ) ;
@@ -921,6 +925,9 @@ axes.calcTicks = function calcTicks(ax, opts) {
921
925
922
926
var tickVals = [ ] ;
923
927
var minorTickVals = [ ] ;
928
+ // all ticks for which labels are drawn which is not necessarily the major ticks when
929
+ // `ticklabelindex` is set.
930
+ var allTicklabelVals = [ ] ;
924
931
925
932
var hasMinor = ax . minor && ( ax . minor . ticks || ax . minor . showgrid ) ;
926
933
@@ -1075,6 +1082,52 @@ axes.calcTicks = function calcTicks(ax, opts) {
1075
1082
}
1076
1083
}
1077
1084
1085
+ // check if ticklabelIndex makes sense, otherwise ignore it
1086
+ if ( ! minorTickVals || minorTickVals . length < 2 ) {
1087
+ ticklabelIndex = false ;
1088
+ } else {
1089
+ var diff = ( minorTickVals [ 1 ] . value - minorTickVals [ 0 ] . value ) * ( isReversed ? - 1 : 1 ) ;
1090
+ if ( ! periodCompatibleWithTickformat ( diff , ax . tickformat ) ) {
1091
+ ticklabelIndex = false ;
1092
+ }
1093
+ }
1094
+ // Determine for which ticks to draw labels
1095
+ if ( ! ticklabelIndex ) {
1096
+ allTicklabelVals = tickVals ;
1097
+ } else {
1098
+ // Collect and sort all major and minor ticks, to find the minor ticks `ticklabelIndex`
1099
+ // steps away from each major tick. For those minor ticks we want to draw the label.
1100
+
1101
+ var allTickVals = tickVals . concat ( minorTickVals ) ;
1102
+ if ( isPeriod && tickVals . length ) {
1103
+ // first major tick was just added for period handling
1104
+ allTickVals = allTickVals . slice ( 1 ) ;
1105
+ }
1106
+
1107
+ allTickVals =
1108
+ allTickVals
1109
+ . sort ( function ( a , b ) { return a . value - b . value ; } )
1110
+ . filter ( function ( tick , index , self ) {
1111
+ return index === 0 || tick . value !== self [ index - 1 ] . value ;
1112
+ } ) ;
1113
+
1114
+ var majorTickIndices =
1115
+ allTickVals
1116
+ . map ( function ( item , index ) {
1117
+ return item . minor === undefined && ! item . skipLabel ? index : null ;
1118
+ } )
1119
+ . filter ( function ( index ) { return index !== null ; } ) ;
1120
+
1121
+ majorTickIndices . forEach ( function ( majorIdx ) {
1122
+ ticklabelIndex . map ( function ( nextLabelIdx ) {
1123
+ var minorIdx = majorIdx + nextLabelIdx ;
1124
+ if ( minorIdx >= 0 && minorIdx < allTickVals . length ) {
1125
+ Lib . pushUnique ( allTicklabelVals , allTickVals [ minorIdx ] ) ;
1126
+ }
1127
+ } ) ;
1128
+ } ) ;
1129
+ }
1130
+
1078
1131
if ( hasMinor ) {
1079
1132
var canOverlap =
1080
1133
( ax . minor . ticks === 'inside' && ax . ticks === 'outside' ) ||
@@ -1108,7 +1161,7 @@ axes.calcTicks = function calcTicks(ax, opts) {
1108
1161
}
1109
1162
}
1110
1163
1111
- if ( isPeriod ) positionPeriodTicks ( tickVals , ax , ax . _definedDelta ) ;
1164
+ if ( isPeriod ) positionPeriodTicks ( allTicklabelVals , ax , ax . _definedDelta ) ;
1112
1165
1113
1166
var i ;
1114
1167
if ( ax . rangebreaks ) {
@@ -1166,38 +1219,44 @@ axes.calcTicks = function calcTicks(ax, opts) {
1166
1219
1167
1220
tickVals = tickVals . concat ( minorTickVals ) ;
1168
1221
1169
- var t , p ;
1222
+ function setTickLabel ( ax , tickVal ) {
1223
+ var text = axes . tickText (
1224
+ ax ,
1225
+ tickVal . value ,
1226
+ false , // hover
1227
+ tickVal . simpleLabel // noSuffixPrefix
1228
+ ) ;
1229
+ var p = tickVal . periodX ;
1230
+ if ( p !== undefined ) {
1231
+ text . periodX = p ;
1232
+ if ( p > maxRange || p < minRange ) { // hide label if outside the range
1233
+ if ( p > maxRange ) text . periodX = maxRange ;
1234
+ if ( p < minRange ) text . periodX = minRange ;
1235
+
1236
+ hideLabel ( text ) ;
1237
+ }
1238
+ }
1239
+ return text ;
1240
+ }
1241
+
1242
+ var t ;
1170
1243
for ( i = 0 ; i < tickVals . length ; i ++ ) {
1171
1244
var _minor = tickVals [ i ] . minor ;
1172
1245
var _value = tickVals [ i ] . value ;
1173
1246
1174
1247
if ( _minor ) {
1175
- minorTicks . push ( {
1176
- x : _value ,
1177
- minor : true
1178
- } ) ;
1248
+ if ( ticklabelIndex && allTicklabelVals . indexOf ( tickVals [ i ] ) !== - 1 ) {
1249
+ t = setTickLabel ( ax , tickVals [ i ] ) ;
1250
+ } else {
1251
+ t = { x : _value } ;
1252
+ }
1253
+ t . minor = true ;
1254
+ minorTicks . push ( t ) ;
1179
1255
} else {
1180
1256
lastVisibleHead = ax . _prevDateHead ;
1181
-
1182
- t = axes . tickText (
1183
- ax ,
1184
- _value ,
1185
- false , // hover
1186
- tickVals [ i ] . simpleLabel // noSuffixPrefix
1187
- ) ;
1188
-
1189
- p = tickVals [ i ] . periodX ;
1190
- if ( p !== undefined ) {
1191
- t . periodX = p ;
1192
- if ( p > maxRange || p < minRange ) { // hide label if outside the range
1193
- if ( p > maxRange ) t . periodX = maxRange ;
1194
- if ( p < minRange ) t . periodX = minRange ;
1195
-
1196
- hideLabel ( t ) ;
1197
- }
1198
- }
1199
-
1200
- if ( tickVals [ i ] . skipLabel ) {
1257
+ t = setTickLabel ( ax , tickVals [ i ] ) ;
1258
+ if ( tickVals [ i ] . skipLabel ||
1259
+ ticklabelIndex && allTicklabelVals . indexOf ( tickVals [ i ] ) === - 1 ) {
1201
1260
hideLabel ( t ) ;
1202
1261
}
1203
1262
@@ -4542,3 +4601,28 @@ function setShiftVal(ax, axShifts) {
4542
4601
axShifts [ ax . overlaying ] [ ax . side ] :
4543
4602
( ax . shift || 0 ) ;
4544
4603
}
4604
+
4605
+ /**
4606
+ * Checks if the given period is at least the period described by the tickformat or larger. If that
4607
+ * is the case, they are compatible, because then the tickformat can be used to describe the period.
4608
+ * E.g. it doesn't make sense to put a year label on a period spanning only a month.
4609
+ * @param {number } period in ms
4610
+ * @param {string } tickformat
4611
+ * @returns {boolean }
4612
+ */
4613
+ function periodCompatibleWithTickformat ( period , tickformat ) {
4614
+ return (
4615
+ / % f / . test ( tickformat ) ? period >= ONEMICROSEC :
4616
+ / % L / . test ( tickformat ) ? period >= ONEMILLI :
4617
+ / % [ S X ] / . test ( tickformat ) ? period >= ONESEC :
4618
+ / % M / . test ( tickformat ) ? period >= ONEMIN :
4619
+ / % [ H I ] / . test ( tickformat ) ? period >= ONEHOUR :
4620
+ / % p / . test ( tickformat ) ? period >= HALFDAY :
4621
+ / % [ A a d e j u w x ] / . test ( tickformat ) ? period >= ONEDAY :
4622
+ / % [ U V W ] / . test ( tickformat ) ? period >= ONEWEEK :
4623
+ / % [ B b m ] / . test ( tickformat ) ? period >= ONEMINMONTH :
4624
+ / % [ q ] / . test ( tickformat ) ? period >= ONEMINQUARTER :
4625
+ / % [ Y y ] / . test ( tickformat ) ? period >= ONEMINYEAR :
4626
+ true
4627
+ ) ;
4628
+ }
0 commit comments