6
6
* the root directory of this source tree.
7
7
*/
8
8
import PropTypes from 'lib/PropTypes' ;
9
- import React from 'react' ;
9
+ import React , { useEffect } from 'react' ;
10
10
import Icon from 'components/Icon/Icon.react' ;
11
11
import styles from 'components/Toolbar/Toolbar.scss' ;
12
+ import Popover from 'components/Popover/Popover.react' ;
13
+ import Position from 'lib/Position' ;
12
14
import { useNavigate , useNavigationType , NavigationType } from 'react-router-dom' ;
13
15
16
+ const POPOVER_CONTENT_ID = 'toolbarStatsPopover' ;
17
+
18
+ const Stats = ( { data } ) => {
19
+ const [ selected , setSelected ] = React . useState ( null ) ;
20
+ const [ open , setOpen ] = React . useState ( false ) ;
21
+ const buttonRef = React . useRef ( ) ;
22
+
23
+ const statsOptions = [
24
+ {
25
+ type : 'sum' ,
26
+ label : 'Sum' ,
27
+ getValue : data => data . reduce ( ( sum , value ) => sum + value , 0 ) ,
28
+ } ,
29
+ {
30
+ type : 'mean' ,
31
+ label : 'Mean' ,
32
+ getValue : data => data . reduce ( ( sum , value ) => sum + value , 0 ) / data . length ,
33
+ } ,
34
+ {
35
+ type : 'count' ,
36
+ label : 'Count' ,
37
+ getValue : data => data . length ,
38
+ } ,
39
+ {
40
+ type : 'p99' ,
41
+ label : 'P99' ,
42
+ getValue : data => {
43
+ const sorted = data . sort ( ( a , b ) => a - b ) ;
44
+ return sorted [ Math . floor ( sorted . length * 0.99 ) ] ;
45
+ } ,
46
+ } ,
47
+ ] ;
48
+
49
+ const toggle = ( ) => {
50
+ setOpen ( ! open ) ;
51
+ } ;
52
+
53
+ const renderPopover = ( ) => {
54
+ const node = buttonRef . current ;
55
+ const position = Position . inDocument ( node ) ;
56
+ return (
57
+ < Popover
58
+ fixed = { true }
59
+ position = { position }
60
+ onExternalClick = { toggle }
61
+ contentId = { POPOVER_CONTENT_ID }
62
+ >
63
+ < div id = { POPOVER_CONTENT_ID } >
64
+ < div
65
+ onClick = { toggle }
66
+ style = { {
67
+ cursor : 'pointer' ,
68
+ width : node . clientWidth ,
69
+ height : node . clientHeight ,
70
+ } }
71
+ > </ div >
72
+ < div className = { styles . stats_popover_container } >
73
+ { statsOptions . map ( item => {
74
+ const itemStyle = [ styles . stats_popover_item ] ;
75
+ if ( item . type === selected ?. type ) {
76
+ itemStyle . push ( styles . active ) ;
77
+ }
78
+ return (
79
+ < div
80
+ key = { item . type }
81
+ className = { itemStyle . join ( ' ' ) }
82
+ onClick = { ( ) => {
83
+ setSelected ( item ) ;
84
+ toggle ( ) ;
85
+ } }
86
+ >
87
+ < span > { item . label } </ span >
88
+ </ div >
89
+ ) ;
90
+ } ) }
91
+ </ div >
92
+ </ div >
93
+ </ Popover >
94
+ ) ;
95
+ } ;
96
+
97
+ useEffect ( ( ) => {
98
+ setSelected ( statsOptions [ 0 ] ) ;
99
+ } , [ ] ) ;
100
+
101
+ return (
102
+ < >
103
+ { selected ? (
104
+ < button ref = { buttonRef } className = { styles . stats } onClick = { toggle } >
105
+ { `${ selected . label } : ${ selected . getValue ( data ) } ` }
106
+ </ button >
107
+ ) : null }
108
+ { open ? renderPopover ( ) : null }
109
+ </ >
110
+ ) ;
111
+ } ;
112
+
14
113
const Toolbar = props => {
15
114
const action = useNavigationType ( ) ;
16
115
const navigate = useNavigate ( ) ;
@@ -34,6 +133,7 @@ const Toolbar = props => {
34
133
</ div >
35
134
</ div >
36
135
</ div >
136
+ { props . selectedData . length ? < Stats data = { props . selectedData } /> : null }
37
137
< div className = { styles . actions } > { props . children } </ div >
38
138
</ div >
39
139
) ;
@@ -44,6 +144,7 @@ Toolbar.propTypes = {
44
144
subsection : PropTypes . string ,
45
145
details : PropTypes . string ,
46
146
relation : PropTypes . object ,
147
+ selectedData : PropTypes . array ,
47
148
} ;
48
149
49
150
export default Toolbar ;
0 commit comments