LCOV - code coverage report
Current view: top level - dashql-app/src/views/query_result - histogram_bars.tsx Coverage Total Hit
Test: DashQL 915828b Lines: 100.0 % 92 92
Test Date: 2026-11-06 19:30:32

            Line data    Source code
       1              : import * as React from 'react';
       2              : import * as d3 from 'd3';
       3              : import { COLUMN_SUMMARY_BASELINE_COLOR } from './data_table_colors.js';
       4              : 
       5              : export const NULL_SYMBOL = "∅";
       6              : 
       7              : interface BarGeometry {
       8              :     x: number;
       9              :     y: number;
      10              :     width: number;
      11              :     height: number;
      12              : }
      13              : 
      14            1 : function getBarGeometry(
      15            1 :     bin: number,
      16            1 :     count: bigint,
      17            1 :     xScale: d3.ScaleBand<string>,
      18            1 :     yScale: d3.ScaleLinear<number, number>,
      19            1 :     height: number,
      20            1 : ): BarGeometry | null {
      21            1 :     const x = xScale(bin.toString());
      22            1 :     const y = yScale(Number(count));
      23            1 :     const width = xScale.bandwidth();
      24            1 :     const barHeight = height - y;
      25            1 :     if (x == null || isNaN(y) || isNaN(width) || isNaN(barHeight)) return null;
      26            1 :     return { x, y, width, height: barHeight };
      27            1 : }
      28              : 
      29              : interface HistogramBarsProps {
      30              :     binCounts: BigInt64Array;
      31              :     filteredBinCounts: BigInt64Array | null;
      32              :     focusedBin: number | null;
      33              :     xScale: d3.ScaleBand<string>;
      34              :     yScale: d3.ScaleLinear<number, number>;
      35              :     height: number;
      36              :     totalBarColor: string;
      37              :     totalBarFocusedColor: string;
      38              :     filteredBarColor: string;
      39              :     filteredBarFocusedColor: string;
      40              : }
      41              : 
      42            1 : function HistogramBarsImpl(props: HistogramBarsProps): React.ReactElement {
      43            1 :     const filteredBinCounts = props.filteredBinCounts;
      44            1 :     const totalBars = Array.from({ length: props.binCounts.length }, (_, bin) => {
      45            1 :         const geometry = getBarGeometry(bin, props.binCounts[bin], props.xScale, props.yScale, props.height);
      46            1 :         return geometry == null ? null : <rect key={bin} {...geometry} style={{ fill: props.totalBarColor }} />;
      47            1 :     });
      48            1 :     const focusedTotalGeometry = props.focusedBin == null
      49            1 :         ? null
      50            1 :         : getBarGeometry(props.focusedBin, props.binCounts[props.focusedBin], props.xScale, props.yScale, props.height);
      51            1 :     const filteredBars = filteredBinCounts == null
      52            1 :         ? null
      53            1 :         : Array.from({ length: filteredBinCounts.length }, (_, bin) => {
      54            1 :             const geometry = getBarGeometry(bin, filteredBinCounts[bin], props.xScale, props.yScale, props.height);
      55            1 :             return geometry == null ? null : <rect key={`filtered-${bin}`} {...geometry} style={{ fill: props.filteredBarColor }} />;
      56            1 :         });
      57            1 :     const focusedFilteredGeometry = props.focusedBin == null || filteredBinCounts == null
      58            1 :         ? null
      59            1 :         : getBarGeometry(props.focusedBin, filteredBinCounts[props.focusedBin], props.xScale, props.yScale, props.height);
      60              : 
      61            1 :     return (
      62            1 :         <>
      63            1 :             {totalBars}
      64            1 :             {focusedTotalGeometry != null && (
      65            1 :                 <rect key={`focused-${props.focusedBin}`} {...focusedTotalGeometry} style={{ fill: props.totalBarFocusedColor }} />
      66              :             )}
      67            1 :             {filteredBars}
      68            1 :             {focusedFilteredGeometry != null && (
      69            1 :                 <rect key={`focused-filtered-${props.focusedBin}`} {...focusedFilteredGeometry} style={{ fill: props.filteredBarFocusedColor }} />
      70              :             )}
      71            1 :         </>
      72              :     );
      73            1 : }
      74              : 
      75            1 : export const HistogramBars = React.memo(HistogramBarsImpl);
      76              : 
      77              : interface HistogramNullBarProps {
      78              :     countNull: number;
      79              :     filteredNullCount: number | null;
      80              :     focused: boolean | null;
      81              :     xScale: d3.ScaleBand<string>;
      82              :     yScale: d3.ScaleLinear<number, number>;
      83              :     xWidth: number;
      84              :     height: number;
      85              :     bottomMargin: number;
      86              :     transformX: number;
      87              :     totalBarColor: string;
      88              :     totalBarFocusedColor: string;
      89              :     filteredBarColor: string;
      90              :     filteredBarFocusedColor: string;
      91              :     onPointerOver: React.MouseEventHandler<SVGGElement>;
      92              :     onPointerOut: React.MouseEventHandler<SVGGElement>;
      93              : }
      94              : 
      95            1 : export function HistogramNullBar(props: HistogramNullBarProps): React.ReactElement | null {
      96            1 :     const nullX = props.xScale(NULL_SYMBOL);
      97            1 :     const nullY = props.yScale(props.countNull);
      98            1 :     const nullWidth = props.xScale.bandwidth();
      99            1 :     const nullHeight = props.height - nullY;
     100            1 :     if (nullX == null || isNaN(nullY) || isNaN(nullWidth) || isNaN(nullHeight)) return null;
     101              : 
     102            1 :     const filteredNullY = props.filteredNullCount == null ? null : props.yScale(props.filteredNullCount);
     103            1 :     const filteredNullHeight = filteredNullY == null ? null : props.height - filteredNullY;
     104            1 :     const hasValidFilteredBar = filteredNullY != null
     105            1 :         && filteredNullHeight != null
     106            1 :         && !isNaN(filteredNullY)
     107            1 :         && !isNaN(filteredNullHeight);
     108              : 
     109            1 :     return (
     110            1 :         <g
     111            1 :             transform={`translate(${props.transformX}, 0)`}
     112            1 :             onPointerOver={props.onPointerOver}
     113            1 :             onPointerMove={props.onPointerOver}
     114            1 :             onPointerOut={props.onPointerOut}
     115              :         >
     116            1 :             <rect
     117            1 :                 x={nullX}
     118            1 :                 y={nullY}
     119            1 :                 width={nullWidth}
     120            1 :                 height={nullHeight}
     121            1 :                 style={{ fill: props.focused ? props.totalBarFocusedColor : props.totalBarColor }}
     122            1 :             />
     123            1 :             {hasValidFilteredBar && (
     124            1 :                 <rect
     125            1 :                     x={nullX}
     126            1 :                     y={filteredNullY!}
     127            1 :                     width={nullWidth}
     128            1 :                     height={filteredNullHeight!}
     129            1 :                     style={{ fill: props.focused ? props.filteredBarFocusedColor : props.filteredBarColor }}
     130            1 :                 />
     131              :             )}
     132            1 :             <g transform={`translate(0, ${props.height})`}>
     133            1 :                 <line
     134            1 :                     x1={0} y1={1}
     135            1 :                     x2={props.xWidth} y2={1}
     136            1 :                     style={{ stroke: COLUMN_SUMMARY_BASELINE_COLOR }}
     137            1 :                 />
     138            1 :                 <rect
     139            1 :                     x={nullX}
     140            1 :                     y={0}
     141            1 :                     width={nullWidth}
     142            1 :                     height={props.bottomMargin}
     143            1 :                     fillOpacity={0}
     144            1 :                 />
     145            1 :             </g>
     146            1 :         </g>
     147              :     );
     148            1 : }
        

Generated by: LCOV version 2.0-1