3
3
secondsInDay ,
4
4
secondsInHour ,
5
5
secondsInMinute ,
6
+ secondsInMonth ,
6
7
secondsInWeek
7
8
} from 'date-fns/constants' ;
8
9
import { last } from 'ramda' ;
@@ -25,85 +26,32 @@ export const generateArrayWithSteps = (n: number, s: number) => {
25
26
}
26
27
const generatedArray = Array . from (
27
28
{ length : Math . floor ( n / s ) + 1 } ,
28
- ( _ , index ) => Number ( ( index * s ) . toFixed ( 0 ) )
29
+ ( _ , index ) => roundDecimalPlaces ( index * s )
29
30
) ;
30
31
const biggestElement = last ( generatedArray ) ;
31
32
if ( biggestElement < n ) {
32
- generatedArray . push ( Number ( ( biggestElement + s ) . toFixed ( 0 ) ) ) ;
33
+ generatedArray . push ( roundDecimalPlaces ( biggestElement + s ) ) ;
33
34
}
34
35
return generatedArray ;
35
36
} ;
36
37
37
38
export type TickArrayOptions = {
38
39
wholeNumbers ?: boolean ;
39
40
isTimeBased ?: boolean ;
41
+ percentageBased ?: boolean ;
40
42
} ;
41
43
42
44
export const createTickArray = (
43
45
data : { x ?: DatumValue ; y : number } [ ] ,
44
46
options : TickArrayOptions = { }
45
47
) => {
46
- const n : number =
47
- data . reduce ( ( acc , cur ) => ( acc > cur . y ? acc : cur . y ) , 0 ) + 1 ;
48
+ const n : number = data . reduce ( ( acc , cur ) => ( acc > cur . y ? acc : cur . y ) , 0 ) ;
48
49
let step = 1 ;
49
50
50
- if ( options . isTimeBased && n > 10 ) {
51
- {
52
- if ( n < 20 ) {
53
- step = 2 ;
54
- } else if ( n < 30 ) {
55
- step = 5 ;
56
- } else if ( n < secondsInMinute ) {
57
- step = 10 ;
58
- } else if ( n < 2 * secondsInMinute ) {
59
- step = 15 ;
60
- } else if ( n < 5 * secondsInMinute ) {
61
- step = 30 ;
62
- } else if ( n < 10 * secondsInMinute ) {
63
- step = secondsInMinute ;
64
- } else if ( n < 20 * secondsInMinute ) {
65
- step = 2 * secondsInMinute ;
66
- } else if ( n < 30 * secondsInMinute ) {
67
- step = 5 * secondsInMinute ;
68
- } else if ( n < secondsInHour ) {
69
- step = 10 * secondsInMinute ;
70
- } else if ( n < 2 * secondsInHour ) {
71
- step = 15 * secondsInMinute ;
72
- } else if ( n < 5 * secondsInHour ) {
73
- step = 30 * secondsInMinute ;
74
- } else if ( n < 10 * secondsInHour ) {
75
- step = secondsInHour ;
76
- } else if ( n < 20 * secondsInHour ) {
77
- step = 2 * secondsInHour ;
78
- } else if ( n < secondsInDay ) {
79
- step = 4 * secondsInHour ;
80
- } else if ( n < 2 * secondsInDay ) {
81
- step = 6 * secondsInHour ;
82
- } else if ( n < 3 * secondsInDay ) {
83
- step = 8 * secondsInHour ;
84
- } else if ( n < secondsInWeek ) {
85
- step = secondsInDay ;
86
- } else if ( n < 2 * secondsInWeek ) {
87
- step = 2 * secondsInDay ;
88
- } else if ( n < 3 * secondsInWeek ) {
89
- step = 3 * secondsInDay ;
90
- } else {
91
- step = secondsInWeek ;
92
- }
93
- return generateArrayWithSteps ( n , step ) ;
94
- }
95
- }
96
- const numberOfDigits = Math . round ( n ) . toString ( ) . length ;
97
- const order = Math . pow ( 10 , numberOfDigits - 1 ) ;
98
-
99
- const twoStep = order * 2 ; // steps of 2, 20, 200...
100
- const fiveStep = order * 5 ; // steps of 5, 50, 500...
101
- const tenStep = order * 10 ; // steps of 10, 100, 1000...
102
- if ( n < twoStep ) step = twoStep / 10 ;
103
- else if ( n < fiveStep ) step = fiveStep / 10 ;
104
- else if ( n < tenStep ) step = tenStep / 10 ;
105
-
106
- if ( options . wholeNumbers ) step = Math . ceil ( step ) ;
51
+ if ( options . isTimeBased && n > 10 ) step = getTimeBasedStep ( n ) ;
52
+ else if ( options . percentageBased && n > 67 ) step = getStep ( n , true ) ;
53
+ else if ( n > 2 || options . wholeNumbers ) step = getStep ( n ) ;
54
+ else step = getSmallStep ( n ) ;
107
55
108
56
return generateArrayWithSteps ( n , step ) ;
109
57
} ;
@@ -126,3 +74,119 @@ export const roundDecimalPlaces = (
126
74
) => {
127
75
return Number ( value ?. toFixed ( roundingDigits ) ) ;
128
76
} ;
77
+
78
+ const getSmallStep = ( n : number ) => {
79
+ const ballPark = n / 3 ; // get roughly around 3-5 steps
80
+ const readableIntervals = [ 0.01 , 0.05 , 0.1 , 0.2 , 0.25 , 0.5 ] ;
81
+ readableIntervals . sort (
82
+ ( x , y ) => Math . abs ( ballPark - x ) - Math . abs ( ballPark - y )
83
+ ) ;
84
+ return readableIntervals [ 0 ] ;
85
+ } ;
86
+
87
+ const getStep = ( n : number , percentageBased : boolean = false ) => {
88
+ const ballPark = n / 3 ; // get roughly around 3-5 steps
89
+ let a , twos ;
90
+ a = twos = 2 ;
91
+
92
+ let b , fives ;
93
+ b = fives = 5 ;
94
+
95
+ let c , tens ;
96
+ c = tens = 10 ;
97
+
98
+ let d , twentyFives ;
99
+ d = twentyFives = 5 ;
100
+
101
+ while ( a <= ballPark ) {
102
+ twos = a ;
103
+ a *= 10 ;
104
+ }
105
+ while ( b <= ballPark ) {
106
+ fives = b ;
107
+ b *= 10 ;
108
+ }
109
+ while ( c <= ballPark ) {
110
+ tens = c ;
111
+ c *= 10 ;
112
+ }
113
+ while ( d <= ballPark ) {
114
+ twentyFives = d ;
115
+ d *= 5 ;
116
+ }
117
+ const readableIntervals = [
118
+ 1 ,
119
+ a ,
120
+ b ,
121
+ c ,
122
+ twos ,
123
+ fives ,
124
+ tens ,
125
+ 15 ,
126
+ twentyFives ,
127
+ 30 ,
128
+ 250
129
+ ] . filter ( ( val ) => {
130
+ if ( ! percentageBased ) return true ;
131
+ if ( n > 67 ) return 100 % val === 0 ;
132
+ return true ;
133
+ } ) ;
134
+
135
+ readableIntervals . sort (
136
+ ( x , y ) => Math . abs ( ballPark - x ) - Math . abs ( ballPark - y )
137
+ ) ;
138
+
139
+ return readableIntervals [ 0 ] ;
140
+ } ;
141
+
142
+ const readableTimeIntervals = [
143
+ 10 ,
144
+ 15 ,
145
+ 20 ,
146
+ 30 ,
147
+ secondsInMinute ,
148
+ 2 * secondsInMinute ,
149
+ 5 * secondsInMinute ,
150
+ 10 * secondsInMinute ,
151
+ 15 * secondsInMinute ,
152
+ 20 * secondsInMinute ,
153
+ 30 * secondsInMinute ,
154
+ 45 * secondsInMinute ,
155
+ secondsInHour ,
156
+ 2 * secondsInHour ,
157
+ 3 * secondsInHour ,
158
+ 4 * secondsInHour ,
159
+ 6 * secondsInHour ,
160
+ 8 * secondsInHour ,
161
+ 12 * secondsInHour ,
162
+ secondsInDay ,
163
+ 2 * secondsInDay ,
164
+ 3 * secondsInDay ,
165
+ 4 * secondsInDay ,
166
+ 5 * secondsInDay ,
167
+ 6 * secondsInDay ,
168
+ 7 * secondsInDay ,
169
+ secondsInWeek ,
170
+ 2 * secondsInWeek ,
171
+ 3 * secondsInWeek ,
172
+ secondsInMonth
173
+ ] ;
174
+
175
+ const getTimeBasedStep = ( n : number ) => {
176
+ const ballPark = n / 3 ; // get roughly around 3-5 steps
177
+
178
+ let i = 0 ;
179
+ let j = 1 ;
180
+
181
+ while ( readableTimeIntervals [ j ] <= ballPark ) {
182
+ i = j ;
183
+ j ++ ;
184
+ }
185
+
186
+ const [ a , b ] = [
187
+ Math . abs ( ballPark - readableTimeIntervals [ i ] ) ,
188
+ Math . abs ( ballPark - readableTimeIntervals [ j ] )
189
+ ] ;
190
+
191
+ return a < b ? readableTimeIntervals [ i ] : readableTimeIntervals [ j ] ;
192
+ } ;
0 commit comments