1
- import { asBoxPlotStats } from '../data' ;
1
+ import { asBoxPlotStats , defaultStatsOptions } from '../data' ;
2
2
import { controllers , helpers , defaults } from 'chart.js' ;
3
- import { baseDefaults , toFixed } from './base' ;
4
- import { boxplotPositioner } from '../tooltip' ;
3
+ import { baseDefaults , configKeys } from './base' ;
5
4
import { BoxAndWiskers , boxOptionsKeys } from '../elements' ;
6
5
7
- function boxplotTooltip ( item , data , ...args ) {
8
- const value = data . datasets [ item . datasetIndex ] . data [ item . index ] ;
9
- const options = this . _chart . getDatasetMeta ( item . datasetIndex ) . controller . _getValueScale ( ) . options . ticks ;
10
- const b = asBoxPlotStats ( value , options ) ;
11
-
12
- const hoveredOutlierIndex = this . _tooltipOutlier == null ? - 1 : this . _tooltipOutlier ;
13
-
14
- const label = this . _options . callbacks . boxplotLabel ;
15
- return label . apply ( this , [ item , data , b , hoveredOutlierIndex ] . concat ( args ) ) ;
16
- }
17
-
18
6
// Chart.defaults.horizontalBoxplot = Chart.helpers.merge({}, [
19
7
// Chart.defaults.horizontalBar,
20
8
// horizontalDefaults,
@@ -23,64 +11,77 @@ function boxplotTooltip(item, data, ...args) {
23
11
24
12
export class BoxPlot extends controllers . bar {
25
13
getMinMax ( scale , canStack ) {
26
- const r = super . getMinMax ( scale , canStack ) ;
27
- // TODO adapt scale.axis to the target stats
28
- return r ;
14
+ const bak = scale . axis ;
15
+ const config = this . _config ;
16
+ scale . axis = config . minStats ;
17
+ const min = super . getMinMax ( scale , canStack ) . min ;
18
+ scale . axis = config . maxStats ;
19
+ const max = super . getMinMax ( scale , canStack ) . max ;
20
+ scale . axis = bak ;
21
+ return { min, max } ;
29
22
}
30
-
31
- parseObjectData ( meta , data , start , count ) {
32
- const r = super . parseObjectData ( meta , data , start , count ) ;
23
+ parseArrayData ( meta , data , start , count ) {
33
24
const vScale = meta . vScale ;
34
25
const iScale = meta . iScale ;
35
26
const labels = iScale . getLabels ( ) ;
27
+ const r = [ ] ;
36
28
for ( let i = 0 ; i < count ; i ++ ) {
37
29
const index = i + start ;
38
- const parsed = r [ i ] ;
30
+ const parsed = { } ;
39
31
parsed [ iScale . axis ] = iScale . parse ( labels [ index ] , index ) ;
40
- const stats = asBoxPlotStats ( data [ index ] ) ; // TODO options
32
+ const stats = asBoxPlotStats ( data [ index ] , this . _config ) ;
41
33
if ( stats ) {
42
34
Object . assign ( parsed , stats ) ;
43
35
parsed [ vScale . axis ] = stats . median ;
44
36
}
37
+ r . push ( parsed ) ;
45
38
}
46
39
return r ;
47
40
}
48
41
42
+ parseObjectData ( meta , data , start , count ) {
43
+ return this . parseArrayData ( meta , data , start , count ) ;
44
+ }
45
+
49
46
getLabelAndValue ( index ) {
50
47
const r = super . getLabelAndValue ( index ) ;
51
48
const vScale = this . _cachedMeta . vScale ;
52
49
const parsed = this . getParsed ( index ) ;
53
50
if ( ! vScale || ! parsed ) {
54
51
return r ;
55
52
}
56
- const v = ( v ) => vScale . getLabelForValue ( v ) ;
57
- r . value = Object . assign (
58
- {
59
- toString ( ) {
60
- // custom to string function for the 'value'
61
- return `(min : ${ v ( this . min ) } , 25% quantile: ${ v ( this . q1 ) } , median: ${ v ( this . median ) } , 75% quantile: ${ v (
62
- this . q3
63
- ) } , max: ${ v ( this . max ) } )` ;
64
- } ,
53
+ r . value = {
54
+ raw : parsed ,
55
+ hoveredOutlierIndex : - 1 ,
56
+ toString ( ) {
57
+ if ( this . hoveredOutlierIndex >= 0 ) {
58
+ return `(outlier : ${ this . outliers [ this . hoveredOutlierIndex ] } )` ;
59
+ }
60
+ // custom to string function for the 'value'
61
+ return `(min: ${ this . min } , 25% quantile: ${ this . q1 } , median: ${ this . median } , 75% quantile: ${ this . q3 } , max: ${ this . max } )` ;
65
62
} ,
66
- parsed
67
- ) ;
63
+ } ;
64
+ this . _transformBoxplot ( r . value , parsed , ( v ) => vScale . getLabelForValue ( v ) ) ;
68
65
return r ;
69
66
}
70
67
68
+ _transformBoxplot ( target , source , mapper ) {
69
+ for ( const key of [ 'min' , 'max' , 'median' , 'q3' , 'q1' , 'whiskerMin' , 'whiskerMax' ] ) {
70
+ target [ key ] = mapper ( source [ key ] ) ;
71
+ }
72
+ for ( const key of [ 'outliers' , 'items' ] ) {
73
+ if ( Array . isArray ( source [ key ] ) ) {
74
+ target [ key ] = source [ key ] . map ( mapper ) ;
75
+ }
76
+ }
77
+ }
78
+
71
79
updateElement ( rectangle , index , properties , mode ) {
72
80
const reset = mode === 'reset' ;
73
81
const scale = this . _cachedMeta . vScale ;
74
82
const parsed = this . getParsed ( index ) ;
75
83
const base = scale . getBasePixel ( ) ;
76
- for ( const key of [ 'median' , 'q3' , 'q1' , 'whiskerMin' , 'whiskerMax' ] ) {
77
- properties [ key ] = reset ? base : scale . getPixelForValue ( parsed [ key ] ) ;
78
- }
79
- for ( const key of [ 'outliers' , 'items' ] ) {
80
- if ( Array . isArray ( parsed [ key ] ) ) {
81
- properties [ key ] = parsed [ key ] . map ( ( v ) => ( reset ? base : scale . getPixelForValue ( v ) ) ) ;
82
- }
83
- }
84
+ this . _transformBoxplot ( properties , parsed , ( v ) => ( reset ? base : scale . getPixelForValue ( v ) ) ) ;
84
85
super . updateElement ( rectangle , index , properties , mode ) ;
85
86
}
86
87
}
@@ -94,48 +95,24 @@ BoxPlot.register = () => {
94
95
BoxPlot . id ,
95
96
helpers . merge ( { } , [
96
97
defaults . bar ,
97
- baseDefaults ,
98
+ baseDefaults ( ) ,
98
99
{
99
- datasets : {
100
- animation : {
101
- numbers : {
102
- type : 'number' ,
103
- properties : defaults . bar . datasets . animation . numbers . properties . concat ( [
104
- 'q1' ,
105
- 'q3' ,
106
- 'min' ,
107
- 'max' ,
108
- 'median' ,
109
- 'whiskerMin' ,
110
- 'whiskerMax' ,
111
- ] ) ,
100
+ datasets : Object . assign (
101
+ {
102
+ minStats : 'min' ,
103
+ maxStats : 'max' ,
104
+ animation : {
105
+ numbers : {
106
+ type : 'number' ,
107
+ properties : defaults . bar . datasets . animation . numbers . properties . concat (
108
+ [ 'q1' , 'q3' , 'min' , 'max' , 'median' , 'whiskerMin' , 'whiskerMax' ] ,
109
+ configKeys . filter ( ( c ) => ! c . endsWith ( 'Color' ) )
110
+ ) ,
111
+ } ,
112
112
} ,
113
113
} ,
114
- } ,
115
- tooltips : {
116
- // position: boxplotPositioner.register().id,
117
- // callbacks: {
118
- // label: boxplotTooltip,
119
- // boxplotLabel(item, data, b, hoveredOutlierIndex) {
120
- // const datasetLabel = data.datasets[item.datasetIndex].label || '';
121
- // let label = `${datasetLabel} ${typeof item.xLabel === 'string' ? item.xLabel : item.yLabel}`;
122
- // if (!b) {
123
- // return `${label} (NaN)`;
124
- // }
125
- // if (hoveredOutlierIndex >= 0) {
126
- // const outlier = b.outliers[hoveredOutlierIndex];
127
- // return `${label} (outlier: ${toFixed.call(this, outlier)})`;
128
- // }
129
- // return `${label} (min: ${toFixed.call(this, b.min)}, q1: ${toFixed.call(
130
- // this,
131
- // b.q1
132
- // )}, median: ${toFixed.call(this, b.median)}, q3: ${toFixed.call(this, b.q3)}, max: ${toFixed.call(
133
- // this,
134
- // b.max
135
- // )})`;
136
- // },
137
- // },
138
- } ,
114
+ defaultStatsOptions
115
+ ) ,
139
116
} ,
140
117
] )
141
118
) ;
0 commit comments