Vikrant Gupta 08e1fd3ca5
feat(trace-details): frontend changes for trace details (#6905)
* feat(trace-details): frontend changes for trace details

* feat(trace-detail): address review comments from elipsis

* feat(trace0-detail): add the new drawer designs

* feat(trace-detail): handle the selected span hover

* feat(trace-detail): address theme colors and span selection

* feat(trace-detail): fix some more css

* feat(trace-detail): fix some more css

* feat(trace-detail): add hoverred span and handled no data components for new drawer

* feat(trace-detail): handle light mode designs

* feat(trace-detail): remove the hover functionality in favor of performance

* feat(trace-detail): span lines connectors

* feat(trace-detail): span lines connectors

* feat(trace-detail): handle the line matching for flamegraph and waterfall

* feat(trace-waterfall): change the timeline color to make it less poky

* feat(trace-waterfall): added where clause support in trace details page

* feat(trace-waterfall): added where clause support in trace details page

* feat(trace-detail): handle light mode designs

* feat(trace-detail): handle light mode designs

* feat(trace-detail): fix build issues

* feat(trace-detail): handle loading error state for filters and flamegraph hovered state

* feat(trace-detail): fix the hardcoded traceID

* feat(trace-detail): remove unnecessaru use effects

* feat(trace-detail): handled the flamegraph update with ID

* feat(trace-detail): added timestamp bucketing and latency sampling

* feat(trace-detail): extract the buckets and span limit in constants

* feat(trace-detail): minor VQA comments

* feat(trace-detail): remove unnecessaru use effects

* feat(trace-detail): add go to related logs

* feat(trace-detail): address review comments

* feat(trace-detail): address review comments

* feat(trace-detail): address review comments

* feat(trace-detail): address review comments
2025-01-28 22:04:24 +05:30

206 lines
5.7 KiB
TypeScript

import './TableV3.styles.scss';
import {
ColumnDef,
flexRender,
getCoreRowModel,
Table,
useReactTable,
} from '@tanstack/react-table';
import { useVirtualizer, Virtualizer } from '@tanstack/react-virtual';
import cx from 'classnames';
import React, {
Dispatch,
MutableRefObject,
SetStateAction,
useEffect,
useMemo,
} from 'react';
// here we are manually rendering the table body so that we can memoize the same for performant re-renders
function TableBody<T>({
table,
virtualizer,
}: {
table: Table<T>;
virtualizer: Virtualizer<HTMLDivElement, Element>;
}): JSX.Element {
const { rows } = table.getRowModel();
return (
<div className="div-tbody">
{virtualizer.getVirtualItems().map((virtualRow, index) => {
const row = rows[virtualRow.index];
return (
<div
key={virtualRow.index}
className="div-tr"
style={{
height: `${virtualRow.size}px`,
transform: `translateY(${virtualRow.start - index * virtualRow.size}px)`,
}}
>
{row.getVisibleCells().map((cell) => (
<div
key={cell.id}
className="div-td"
// we are manually setting the column width here based on the calculated column vars
style={{
width: `calc(var(--col-${cell.column.id}-size) * 1px)`,
}}
>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</div>
))}
</div>
);
})}
</div>
);
}
// memoize the table body based on the data object being passed to the table
const MemoizedTableBody = React.memo(
TableBody,
(prev, next) => prev.table.options.data === next.table.options.data,
) as typeof TableBody;
interface ITableConfig {
defaultColumnMinSize?: number;
defaultColumnMaxSize?: number;
handleVirtualizerInstanceChanged?: (
instance: Virtualizer<HTMLDivElement, Element>,
) => void;
}
interface ITableV3Props<T> {
columns: ColumnDef<T, any>[];
data: T[];
config: ITableConfig;
customClassName?: string;
setColumnWidths: Dispatch<SetStateAction<number>>;
virtualiserRef?: MutableRefObject<
Virtualizer<HTMLDivElement, Element> | undefined
>;
}
export function TableV3<T>(props: ITableV3Props<T>): JSX.Element {
const {
data,
columns,
config,
customClassName = '',
virtualiserRef,
setColumnWidths,
} = props;
const table = useReactTable({
data,
columns,
defaultColumn: {
minSize: config.defaultColumnMinSize,
maxSize: config.defaultColumnMaxSize,
},
columnResizeMode: 'onChange',
getCoreRowModel: getCoreRowModel(),
// turn on debug flags to get debug logs from these instances
debugAll: false,
});
const tableRef = React.useRef<HTMLDivElement>(null);
const { rows } = table.getRowModel();
const virtualizer = useVirtualizer({
count: rows.length,
getScrollElement: () => tableRef.current,
estimateSize: () => 54,
overscan: 20,
onChange: config.handleVirtualizerInstanceChanged,
});
useEffect(() => {
if (virtualiserRef) {
virtualiserRef.current = virtualizer;
}
}, [virtualiserRef, virtualizer]);
/**
* Instead of calling `column.getSize()` on every render for every header
* and especially every data cell (very expensive),
* we will calculate all column sizes at once at the root table level in a useMemo
* and pass the column sizes down as CSS variables to the <table> element.
*/
const columnSizeVars = useMemo(() => {
const headers = table.getFlatHeaders();
const colSizes: { [key: string]: number } = {};
for (let i = 0; i < headers.length; i++) {
const header = headers[i]!;
colSizes[`--header-${header.id}-size`] = header.getSize();
colSizes[`--col-${header.column.id}-size`] = header.column.getSize();
}
return colSizes;
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [table.getState().columnSizingInfo, table.getState().columnSizing]);
useEffect(() => {
const headers = table.getFlatHeaders();
setColumnWidths(headers[0].getSize());
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [table.getState().columnSizingInfo, table.getState().columnSizing]);
return (
<div className={cx('p-2', customClassName)} ref={tableRef}>
{/* Here in the <table> equivalent element (surrounds all table head and data cells), we will define our CSS variables for column sizes */}
<div
className="div-table"
style={{
...columnSizeVars, // Define column sizes on the <table> element
width: table.getTotalSize(),
height: `${virtualizer.getTotalSize()}px`,
}}
>
<div className="div-thead">
{table.getHeaderGroups().map((headerGroup) => (
<div key={headerGroup.id} className="div-tr">
{headerGroup.headers.map((header) => (
<div
key={header.id}
className="div-th"
style={{
width: `calc(var(--header-${header?.id}-size) * 1px)`,
}}
>
{header.isPlaceholder
? null
: flexRender(header.column.columnDef.header, header.getContext())}
<div
{...{
onDoubleClick: (): void => header.column.resetSize(),
onMouseDown: header.getResizeHandler(),
onTouchStart: header.getResizeHandler(),
style: {
display: !header.column.getCanResize() ? 'none' : '',
},
className: `resizer ${
header.column.getIsResizing() ? 'isResizing' : ''
}`,
}}
/>
</div>
))}
</div>
))}
</div>
{/* When resizing any column we will render this special memoized version of our table body */}
{table.getState().columnSizingInfo.isResizingColumn ? (
<MemoizedTableBody table={table} virtualizer={virtualizer} />
) : (
<TableBody table={table} virtualizer={virtualizer} />
)}
</div>
</div>
);
}
TableV3.defaultProps = {
customClassName: '',
virtualiserRef: null,
};