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

            Line data    Source code
       1              : import * as React from 'react';
       2              : 
       3              : import type { QueryExecutionState } from '../../query/query_execution_state.js';
       4              : import { useComputationRegistry } from '../../compute/computation_registry.js';
       5              : import type { ComputationAction, TableComputationState } from '../../compute/computation_state.js';
       6              : import type { Dispatch } from '../../utils/variant.js';
       7              : import { QueryResultSearchControls } from './query_result_search_controls.js';
       8              : import { useQueryResultSearch } from './use_query_result_search.js';
       9              : import { forEachIntegerValue } from './visible_rows.js';
      10              : 
      11              : interface Props {
      12              :     query: QueryExecutionState;
      13              :     children?: React.ReactNode;
      14              : }
      15              : 
      16            1 : export function QueryResultToolbar(props: Props): React.ReactNode {
      17            1 :     const [computationState, dispatch] = useComputationRegistry();
      18            1 :     const table = computationState.tableComputations[props.query.queryId] ?? null;
      19            1 :     return <TableSearchToolbar table={table} dispatch={dispatch}>{props.children}</TableSearchToolbar>;
      20            1 : }
      21              : 
      22              : interface TableSearchToolbarProps {
      23              :     table: TableComputationState | null;
      24              :     dispatch: Dispatch<ComputationAction>;
      25              :     children?: React.ReactNode;
      26              : }
      27              : 
      28              : /// The column and data search of any analyzed table, not only of query results
      29            1 : export function TableSearchToolbar(props: TableSearchToolbarProps) {
      30            1 :     const { table, dispatch } = props;
      31            1 :     const search = useQueryResultSearch(table, dispatch);
      32            1 :     if (search.columnSearch == null || search.dataSearch == null) return props.children;
      33            1 :     const dataMatchCount = getMatchingRowCount(table);
      34            1 :     return (
      35            1 :         <>
      36            1 :             <QueryResultSearchControls
      37            1 :                 columnSearch={search.columnSearch}
      38            1 :                 dataSearch={search.dataSearch}
      39            1 :                 onColumnPatternChange={search.setColumnPattern}
      40            1 :                 onDataPatternChange={search.setDataPattern}
      41            1 :                 columnMatchCount={search.columnSearch.matchingColumnGroups?.length ?? null}
      42            1 :                 dataMatchCount={dataMatchCount}
      43            1 :             />
      44            1 :             {props.children}
      45            1 :         </>
      46              :     );
      47            1 : }
      48              : 
      49            1 : export function useQueryResultRowCounts(query: QueryExecutionState | null) {
      50            1 :     const [computationState] = useComputationRegistry();
      51            1 :     const table = query == null ? null : computationState.tableComputations[query.queryId] ?? null;
      52            1 :     const totalRows = table?.dataTable.numRows ?? null;
      53            1 :     const matchingRows = getMatchingRowCount(table);
      54            1 :     const currentRows = matchingRows ?? table?.filterTable?.dataTable.numRows ?? totalRows;
      55            1 :     return { totalRows, currentRows, matchingRows };
      56            1 : }
      57              : 
      58              : /// Matching row counts per filter table and data search matches.
      59              : /// Weakly keyed by both, so a count never outlives the tables it was computed from.
      60            1 : const matchingRowCounts = new WeakMap<object, WeakMap<object, number>>();
      61              : 
      62            1 : export function getMatchingRowCount(table: ReturnType<typeof useComputationRegistry>[0]['tableComputations'][number] | null): number | null {
      63            1 :     const dataMatches = table?.dataSearch?.matchingRows;
      64            1 :     if (table == null || dataMatches == null) return null;
      65            0 :     if (table.filterTable == null) return dataMatches.size;
      66            0 :     const filterDataTable = table.filterTable.dataTable;
      67            0 :     let countsByMatches = matchingRowCounts.get(filterDataTable);
      68            1 :     const cached = countsByMatches?.get(dataMatches);
      69            1 :     if (cached !== undefined) return cached;
      70            0 :     const rowIds = filterDataTable.getChildAt(0);
      71            0 :     if (rowIds == null) return 0;
      72            0 :     let count = 0;
      73            0 :     forEachIntegerValue(rowIds, rowId => {
      74            0 :         if (dataMatches.has(rowId)) ++count;
      75            0 :     });
      76            0 :     if (countsByMatches == null) {
      77            0 :         countsByMatches = new WeakMap();
      78            0 :         matchingRowCounts.set(filterDataTable, countsByMatches);
      79            0 :     }
      80            0 :     countsByMatches.set(dataMatches, count);
      81            0 :     return count;
      82            1 : }
        

Generated by: LCOV version 2.0-1