11import React from 'react' ;
22
3- import { Checkbox , Select } from '@gravity-ui/uikit' ;
3+ import { Checkbox , Popup , Select } from '@gravity-ui/uikit' ;
44
55import { ResponseError } from '../../components/Errors/ResponseError' ;
66import { Loader } from '../../components/Loader' ;
7+ import { TabletTooltipContent } from '../../components/TooltipsContent' ;
78import { heatmapApi , setHeatmapOptions } from '../../store/reducers/heatmap' ;
8- import { hideTooltip , showTooltip } from '../../store/reducers/tooltip' ;
9- import type { IHeatmapMetricValue } from '../../types/store/heatmap' ;
9+ import type { IHeatmapMetricValue , IHeatmapTabletData } from '../../types/store/heatmap' ;
1010import { cn } from '../../utils/cn' ;
1111import { EMPTY_DATA_PLACEHOLDER } from '../../utils/constants' ;
1212import { formatNumber } from '../../utils/dataFormatters/dataFormatters' ;
@@ -32,6 +32,14 @@ export const Heatmap = ({path, database, databaseFullPath}: HeatmapProps) => {
3232
3333 const itemsContainer = React . useRef < HTMLDivElement | null > ( null ) ;
3434
35+ const [ tabletTooltip , setTabletTooltip ] = React . useState < {
36+ tablet : IHeatmapTabletData ;
37+ position : { left : number ; top : number } ;
38+ } | null > ( null ) ;
39+ const [ tabletTooltipAnchorElement , setTabletTooltipAnchorElement ] =
40+ React . useState < HTMLDivElement | null > ( null ) ;
41+ const isTabletTooltipHoveredRef = React . useRef ( false ) ;
42+
3543 const [ autoRefreshInterval ] = useAutoRefreshInterval ( ) ;
3644
3745 const { currentData, isFetching, error} = heatmapApi . useGetHeatmapTabletsInfoQuery (
@@ -44,13 +52,35 @@ export const Heatmap = ({path, database, databaseFullPath}: HeatmapProps) => {
4452 const { tablets = [ ] , metrics} = currentData || { } ;
4553 const { sort, heatmap, currentMetric} = useTypedSelector ( ( state ) => state . heatmap ) ;
4654
47- const onShowTooltip = ( ...args : Parameters < typeof showTooltip > ) => {
48- dispatch ( showTooltip ( ...args ) ) ;
49- } ;
55+ const handleShowTabletTooltip = React . useCallback (
56+ ( tablet : IHeatmapTabletData , position : { left : number ; top : number } ) => {
57+ setTabletTooltip ( { tablet, position} ) ;
58+ } ,
59+ [ ] ,
60+ ) ;
5061
51- const onHideTooltip = ( ) => {
52- dispatch ( hideTooltip ( ) ) ;
53- } ;
62+ const handleHideTabletTooltip = React . useCallback ( ( ) => {
63+ setTabletTooltip ( null ) ;
64+ } , [ ] ) ;
65+
66+ const handleRequestHideTabletTooltip = React . useCallback ( ( ) => {
67+ setTabletTooltip ( ( prev ) => {
68+ if ( ! prev || isTabletTooltipHoveredRef . current ) {
69+ return prev ;
70+ }
71+
72+ return null ;
73+ } ) ;
74+ } , [ ] ) ;
75+
76+ const handleTooltipMouseEnter = React . useCallback ( ( ) => {
77+ isTabletTooltipHoveredRef . current = true ;
78+ } , [ ] ) ;
79+
80+ const handleTooltipMouseLeave = React . useCallback ( ( ) => {
81+ isTabletTooltipHoveredRef . current = false ;
82+ handleHideTabletTooltip ( ) ;
83+ } , [ handleHideTabletTooltip ] ) ;
5484
5585 const handleMetricChange = ( value : string [ ] ) => {
5686 dispatch (
@@ -76,14 +106,7 @@ export const Heatmap = ({path, database, databaseFullPath}: HeatmapProps) => {
76106 } ;
77107
78108 const renderHistogram = ( ) => {
79- return (
80- < Histogram
81- tablets = { tablets }
82- currentMetric = { currentMetric }
83- showTooltip = { onShowTooltip }
84- hideTooltip = { onHideTooltip }
85- />
86- ) ;
109+ return < Histogram tablets = { tablets } currentMetric = { currentMetric } /> ;
87110 } ;
88111
89112 const renderHeatmapCanvas = ( ) => {
@@ -108,18 +131,30 @@ export const Heatmap = ({path, database, databaseFullPath}: HeatmapProps) => {
108131
109132 return (
110133 < div ref = { itemsContainer } className = { b ( 'items' ) } >
134+ { tabletTooltip ? (
135+ < div
136+ key = { `${ tabletTooltip . position . left } -${ tabletTooltip . position . top } ` }
137+ ref = { setTabletTooltipAnchorElement }
138+ className = { b ( 'tooltip-anchor' ) }
139+ style = { {
140+ left : tabletTooltip . position . left ,
141+ top : tabletTooltip . position . top ,
142+ } }
143+ />
144+ ) : null }
111145 < HeatmapCanvas
112146 tablets = { sortedTablets }
113147 parentRef = { itemsContainer }
114- showTooltip = { onShowTooltip }
115- hideTooltip = { onHideTooltip }
148+ onShowTabletTooltip = { handleShowTabletTooltip }
149+ onHideTabletTooltip = { handleRequestHideTabletTooltip }
116150 />
117151 </ div >
118152 ) ;
119153 } ;
120154
121155 const renderContent = ( ) => {
122156 const { min, max} = getCurrentMetricLimits ( currentMetric , tablets ) ;
157+ const isTabletTooltipPopupOpen = Boolean ( tabletTooltip && tabletTooltipAnchorElement ) ;
123158
124159 let content ;
125160 if ( ! error || currentData ) {
@@ -128,6 +163,23 @@ export const Heatmap = ({path, database, databaseFullPath}: HeatmapProps) => {
128163
129164 return (
130165 < div className = { b ( ) } >
166+ { isTabletTooltipPopupOpen ? (
167+ < Popup
168+ open
169+ hasArrow
170+ placement = { [ 'top' , 'bottom' , 'left' , 'right' ] }
171+ anchorElement = { tabletTooltipAnchorElement }
172+ onOutsideClick = { handleHideTabletTooltip }
173+ >
174+ < div
175+ className = { b ( 'tooltip' ) }
176+ onMouseEnter = { handleTooltipMouseEnter }
177+ onMouseLeave = { handleTooltipMouseLeave }
178+ >
179+ < TabletTooltipContent data = { tabletTooltip ?. tablet } />
180+ </ div >
181+ </ Popup >
182+ ) : null }
131183 < div className = { b ( 'filters' ) } >
132184 < Select
133185 className = { b ( 'heatmap-select' ) }
0 commit comments