diff --git a/src/components/QueryResultTable/Cell/Cell.tsx b/src/components/QueryResultTable/Cell/Cell.tsx index 2df43422b2..1c87e008ed 100644 --- a/src/components/QueryResultTable/Cell/Cell.tsx +++ b/src/components/QueryResultTable/Cell/Cell.tsx @@ -1,7 +1,7 @@ import React from 'react'; -import {showTooltip} from '../../../store/reducers/tooltip'; -import {useTypedDispatch} from '../../../utils/hooks'; +import {Popup} from '@gravity-ui/uikit'; + import {b} from '../QueryResultTable'; interface CellProps { @@ -12,14 +12,31 @@ interface CellProps { export const Cell = React.memo(function Cell(props: CellProps) { const {className, value} = props; - const dispatch = useTypedDispatch(); + const [open, setOpen] = React.useState(false); + const anchorRef = React.useRef(null); + + const handleToggle = React.useCallback(() => { + setOpen((prevOpen) => !prevOpen); + }, []); + + const handleClose = React.useCallback(() => { + setOpen(false); + }, []); return ( - dispatch(showTooltip(e.target, value, 'cell'))} - > - {value} - + + +
{value}
+
+ + {value} + +
); }); diff --git a/src/components/QueryResultTable/QueryResultTable.scss b/src/components/QueryResultTable/QueryResultTable.scss index ee984ff469..99bd990e22 100644 --- a/src/components/QueryResultTable/QueryResultTable.scss +++ b/src/components/QueryResultTable/QueryResultTable.scss @@ -6,6 +6,13 @@ @include mixins.cell-container; } + &__cell-popup { + max-width: 300px; + padding: 10px; + + word-break: break-word; + } + &__message { padding: 15px 10px; } diff --git a/src/components/QueryResultTable/QueryResultTable.tsx b/src/components/QueryResultTable/QueryResultTable.tsx index a7d7c1f7f7..10f4160c39 100644 --- a/src/components/QueryResultTable/QueryResultTable.tsx +++ b/src/components/QueryResultTable/QueryResultTable.tsx @@ -28,7 +28,7 @@ export const b = cn('ydb-query-result-table'); const WIDTH_PREDICTION_ROWS_COUNT = 100; -const prepareTypedColumns = (columns: ColumnType[], data?: KeyValueRow[]) => { +const prepareTypedColumns = (columns: ColumnType[], data: KeyValueRow[] | undefined) => { if (!columns.length) { return []; } @@ -49,7 +49,7 @@ const prepareTypedColumns = (columns: ColumnType[], data?: KeyValueRow[]) => { }); }; -const prepareGenericColumns = (data?: KeyValueRow[]) => { +const prepareGenericColumns = (data: KeyValueRow[] | undefined) => { if (!data?.length) { return []; } @@ -85,7 +85,7 @@ export const QueryResultTable = (props: QueryResultTableProps) => { const preparedColumns = React.useMemo(() => { return columns ? prepareTypedColumns(columns, data) : prepareGenericColumns(data); - }, [data, columns]); + }, [columns, data]); const settings = React.useMemo(() => { return { diff --git a/src/containers/App/App.tsx b/src/containers/App/App.tsx index fcbbc1b2c4..367c5518aa 100644 --- a/src/containers/App/App.tsx +++ b/src/containers/App/App.tsx @@ -7,7 +7,6 @@ import {Helmet} from 'react-helmet-async'; import {componentsRegistry} from '../../components/ComponentsProvider/componentsRegistry'; import {FullscreenProvider} from '../../components/Fullscreen/FullscreenContext'; import {useTypedSelector} from '../../utils/hooks'; -import ReduxTooltip from '../ReduxTooltip/ReduxTooltip'; import type {YDBEmbeddedUISettings} from '../UserSettings/settings'; import {useAppTitle} from './AppTitleContext'; @@ -34,7 +33,6 @@ function App({store, history, children, userSettings, appTitle = defaultAppTitle {children} {ChatPanel && } - ); } diff --git a/src/containers/Heatmap/Heatmap.scss b/src/containers/Heatmap/Heatmap.scss index ea1d7dd330..02c0f6c932 100644 --- a/src/containers/Heatmap/Heatmap.scss +++ b/src/containers/Heatmap/Heatmap.scss @@ -51,6 +51,8 @@ } &__items { + position: relative; + overflow: auto; } &__canvas-container { @@ -59,6 +61,19 @@ cursor: pointer; } + &__tooltip-anchor { + position: absolute; + + width: 1px; + height: 1px; + + pointer-events: none; + } + + &__tooltip { + padding: 10px; + } + &__filters { display: flex; align-items: center; diff --git a/src/containers/Heatmap/Heatmap.tsx b/src/containers/Heatmap/Heatmap.tsx index 4e988d2215..6b096305a6 100644 --- a/src/containers/Heatmap/Heatmap.tsx +++ b/src/containers/Heatmap/Heatmap.tsx @@ -1,12 +1,12 @@ import React from 'react'; -import {Checkbox, Select} from '@gravity-ui/uikit'; +import {Checkbox, Popup, Select} from '@gravity-ui/uikit'; import {ResponseError} from '../../components/Errors/ResponseError'; import {Loader} from '../../components/Loader'; +import {TabletTooltipContent} from '../../components/TooltipsContent'; import {heatmapApi, setHeatmapOptions} from '../../store/reducers/heatmap'; -import {hideTooltip, showTooltip} from '../../store/reducers/tooltip'; -import type {IHeatmapMetricValue} from '../../types/store/heatmap'; +import type {IHeatmapMetricValue, IHeatmapTabletData} from '../../types/store/heatmap'; import {cn} from '../../utils/cn'; import {EMPTY_DATA_PLACEHOLDER} from '../../utils/constants'; import {formatNumber} from '../../utils/dataFormatters/dataFormatters'; @@ -32,6 +32,14 @@ export const Heatmap = ({path, database, databaseFullPath}: HeatmapProps) => { const itemsContainer = React.createRef(); + const [tabletTooltip, setTabletTooltip] = React.useState<{ + tablet: IHeatmapTabletData; + position: {left: number; top: number}; + } | null>(null); + const [tabletTooltipAnchorElement, setTabletTooltipAnchorElement] = + React.useState(null); + const isTabletTooltipHoveredRef = React.useRef(false); + const [autoRefreshInterval] = useAutoRefreshInterval(); const {currentData, isFetching, error} = heatmapApi.useGetHeatmapTabletsInfoQuery( @@ -44,13 +52,35 @@ export const Heatmap = ({path, database, databaseFullPath}: HeatmapProps) => { const {tablets = [], metrics} = currentData || {}; const {sort, heatmap, currentMetric} = useTypedSelector((state) => state.heatmap); - const onShowTooltip = (...args: Parameters) => { - dispatch(showTooltip(...args)); - }; + const handleShowTabletTooltip = React.useCallback( + (tablet: IHeatmapTabletData, position: {left: number; top: number}) => { + setTabletTooltip({tablet, position}); + }, + [], + ); - const onHideTooltip = () => { - dispatch(hideTooltip()); - }; + const handleHideTabletTooltip = React.useCallback(() => { + setTabletTooltip(null); + }, []); + + const handleRequestHideTabletTooltip = React.useCallback(() => { + setTabletTooltip((prev) => { + if (!prev || isTabletTooltipHoveredRef.current) { + return prev; + } + + return null; + }); + }, []); + + const handleTooltipMouseEnter = React.useCallback(() => { + isTabletTooltipHoveredRef.current = true; + }, []); + + const handleTooltipMouseLeave = React.useCallback(() => { + isTabletTooltipHoveredRef.current = false; + handleHideTabletTooltip(); + }, [handleHideTabletTooltip]); const handleMetricChange = (value: string[]) => { dispatch( @@ -76,14 +106,7 @@ export const Heatmap = ({path, database, databaseFullPath}: HeatmapProps) => { }; const renderHistogram = () => { - return ( - - ); + return ; }; const renderHeatmapCanvas = () => { @@ -108,11 +131,22 @@ export const Heatmap = ({path, database, databaseFullPath}: HeatmapProps) => { return (
+ {tabletTooltip ? ( +
+ ) : null}
); @@ -128,6 +162,23 @@ export const Heatmap = ({path, database, databaseFullPath}: HeatmapProps) => { return (
+ {tabletTooltip ? ( + +
+ +
+
+ ) : null}