15
15
*/
16
16
17
17
import React from 'react'
18
- import { Chart , ChartAxis , ChartBar , ChartLabel , ChartVoronoiContainer } from '@patternfly/react-charts '
18
+ import stringColoring from 'string-similarity-coloring '
19
19
import { REPL , Row , Tab , Table } from '@kui-shell/core'
20
+ import { Chart , ChartAxis , ChartBar , ChartLabel , ChartVoronoiContainer } from '@patternfly/react-charts'
20
21
21
22
interface Props {
22
23
response : Table
@@ -31,6 +32,7 @@ interface State {
31
32
rows : Row [ ]
32
33
animate : boolean
33
34
counts : number [ ]
35
+ colors : string [ ]
34
36
scale : 'linear' | 'log'
35
37
}
36
38
@@ -62,24 +64,25 @@ function sameState(A: State, B: State): boolean {
62
64
export default class Histogram extends React . PureComponent < Props , State > {
63
65
private readonly horizontal = true
64
66
private readonly barHeight = 10
65
- private readonly axisLabelFontSize = 9
67
+ private readonly barSpacing = 0.1 // 10% of barHeight spacing between bars
68
+ private readonly axisLabelFontSize = 0.9 * this . barHeight
66
69
private readonly minAxisLabelChars = 4
67
70
private readonly maxAxisLabelChars = 13
68
- private readonly barLabelFontSize = 6
71
+ private readonly barLabelFontSize = 0.65 * this . barHeight
69
72
private readonly minBarLabelChars = 1
70
73
private readonly maxBarLabelChars = 6
71
74
72
75
public constructor ( props : Props ) {
73
76
super ( props )
74
- this . state = Histogram . filterRows ( props . response . body )
77
+ this . state = Histogram . filterRows ( props . response . body , props . response . colorBy !== undefined )
75
78
}
76
79
77
80
public componentDidCatch ( error : Error , errorInfo : React . ErrorInfo ) {
78
81
console . error ( error , errorInfo )
79
82
}
80
83
81
84
public static getDerivedStateFromProps ( props : Props , state : State ) {
82
- const newState = Histogram . filterRows ( props . response . body )
85
+ const newState = Histogram . filterRows ( props . response . body , props . response . colorBy !== undefined )
83
86
84
87
// to avoid re-renders when nothing has changed...
85
88
if ( state && sameState ( state , newState ) ) {
@@ -94,17 +97,42 @@ export default class Histogram extends React.PureComponent<Props, State> {
94
97
}
95
98
}
96
99
97
- private static filterRows ( rows : Props [ 'response' ] [ 'body' ] ) : State {
100
+ private static filterRows ( rows : Props [ 'response' ] [ 'body' ] , useFancyColoring : boolean ) : State {
98
101
const countOf = ( row : Row ) => parseInt ( row . attributes . find ( _ => _ . key && / ^ c o u n t $ / i. test ( _ . key ) ) . value , 10 )
99
102
const counts = rows . map ( countOf )
100
103
const yMax = counts . reduce ( ( yMax , count ) => Math . max ( yMax , count ) , 0 )
101
- const filteredRows = rows . filter ( ( row , ridx ) => ( ! Histogram . isTiny ( counts [ ridx ] , yMax ) ? 1 : 0 ) )
102
104
103
- return {
104
- animate : true ,
105
- rows : filteredRows ,
106
- counts : filteredRows . map ( countOf ) ,
107
- scale : filteredRows . length === rows . length ? 'linear' : 'log'
105
+ const filteredRowsPriorToSorting = rows . filter ( ( row , ridx ) => ( ! Histogram . isTiny ( counts [ ridx ] , yMax ) ? 1 : 0 ) )
106
+
107
+ if ( ! useFancyColoring ) {
108
+ const filteredRows = filteredRowsPriorToSorting
109
+ return {
110
+ animate : true ,
111
+ rows : filteredRows ,
112
+ counts : filteredRows . map ( countOf ) ,
113
+ scale : filteredRows . length === rows . length ? 'linear' : 'log' ,
114
+ colors : undefined
115
+ }
116
+ } else {
117
+ // assign colors to the rows, and then sort the rows to group
118
+ // nearby colors(in the color space) so that they are also close
119
+ // geographically
120
+ const sortedByCount = filteredRowsPriorToSorting . slice ( ) . sort ( ( a , b ) => countOf ( b ) - countOf ( a ) )
121
+ const colors = stringColoring ( sortedByCount . map ( _ => _ . rowKey || _ . name ) )
122
+
123
+ const filteredRowsForSorting = sortedByCount
124
+ . map ( ( row , idx ) => ( { row, color : colors [ idx ] } ) )
125
+ . sort ( ( a , b ) => b . color . primary - a . color . primary || b . color . secondary - a . color . secondary )
126
+
127
+ const filteredRows = filteredRowsForSorting . map ( _ => _ . row )
128
+
129
+ return {
130
+ animate : true ,
131
+ rows : filteredRows ,
132
+ counts : filteredRows . map ( countOf ) ,
133
+ scale : filteredRows . length === rows . length ? 'linear' : 'log' ,
134
+ colors : filteredRowsForSorting . map ( _ => _ . color . color )
135
+ }
108
136
}
109
137
}
110
138
@@ -140,15 +168,15 @@ export default class Histogram extends React.PureComponent<Props, State> {
140
168
< Chart
141
169
animate = { this . state . animate && { onLoad : { duration : 0 } , duration : 200 } }
142
170
domainPadding = { 10 }
143
- height = { this . state . rows . length * this . barHeight * 1.375 }
171
+ height = { this . state . rows . length * this . barHeight * ( 1 + this . barSpacing ) }
144
172
horizontal = { this . horizontal }
145
173
padding = { {
146
174
left : this . leftPad ( ) ,
147
175
right : this . rightPad ( ) ,
148
176
top : 0 ,
149
177
bottom : 0
150
178
} }
151
- containerComponent = { < ChartVoronoiContainer labels = { _ => ` ${ _ . datum . x } : ${ _ . datum . y } ` } constrainToVisibleArea /> }
179
+ containerComponent = { < ChartVoronoiContainer constrainToVisibleArea /> }
152
180
>
153
181
{ this . axis ( ) }
154
182
{ this . bars ( ) }
@@ -181,7 +209,13 @@ export default class Histogram extends React.PureComponent<Props, State> {
181
209
barWidth = { this . barHeight }
182
210
scale = { { y : this . state . scale } }
183
211
style = { {
184
- data : { fill : 'var(--color-base05)' , stroke : 'var(--color-base04)' , strokeWidth : 0.5 } ,
212
+ data : {
213
+ fill : ! this . state . colors
214
+ ? 'var(--color-base05)'
215
+ : ( { index } ) => this . state . colors [ index ] || 'var(--color-base05)' ,
216
+ stroke : ! this . state . colors ? 'var(--color-base04)' : undefined ,
217
+ strokeWidth : 0.5
218
+ } ,
185
219
labels : { fontFamily : 'var(--font-sans-serif)' , fontSize : this . barLabelFontSize }
186
220
} }
187
221
data = { this . state . rows . map ( ( row , ridx ) => ( {
0 commit comments