66 * the root directory of this source tree.
77 */
88import PropTypes from 'lib/PropTypes' ;
9- import React from 'react' ;
9+ import React , { useEffect } from 'react' ;
1010import Icon from 'components/Icon/Icon.react' ;
1111import styles from 'components/Toolbar/Toolbar.scss' ;
12+ import Popover from 'components/Popover/Popover.react' ;
13+ import Position from 'lib/Position' ;
1214import { useNavigate , useNavigationType , NavigationType } from 'react-router-dom' ;
1315
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+
14113const Toolbar = props => {
15114 const action = useNavigationType ( ) ;
16115 const navigate = useNavigate ( ) ;
@@ -34,6 +133,7 @@ const Toolbar = props => {
34133 </ div >
35134 </ div >
36135 </ div >
136+ { props . selectedData . length ? < Stats data = { props . selectedData } /> : null }
37137 < div className = { styles . actions } > { props . children } </ div >
38138 </ div >
39139 ) ;
@@ -44,6 +144,7 @@ Toolbar.propTypes = {
44144 subsection : PropTypes . string ,
45145 details : PropTypes . string ,
46146 relation : PropTypes . object ,
147+ selectedData : PropTypes . array ,
47148} ;
48149
49150export default Toolbar ;
0 commit comments