diff --git a/static/app/views/explore/components/table.tsx b/static/app/views/explore/components/table.tsx index 57cfd1484e71f9..80cae4ce0c6b69 100644 --- a/static/app/views/explore/components/table.tsx +++ b/static/app/views/explore/components/table.tsx @@ -65,8 +65,9 @@ export function useTableStyles( tableRef: React.RefObject, options?: { minimumColumnWidth?: number; + onResizeEnd?: () => void; prefixColumnWidth?: 'min-content' | number; - staticColumnWidths?: Record; + staticColumnWidths?: Record; } ) { const minimumColumnWidth = options?.minimumColumnWidth ?? MINIMUM_COLUMN_WIDTH; @@ -74,6 +75,8 @@ export function useTableStyles( defined(options?.prefixColumnWidth) && typeof options.prefixColumnWidth === 'number' ? `${options.prefixColumnWidth}px` : options?.prefixColumnWidth; + const staticColumnWidths = options?.staticColumnWidths; + const onResizeEnd = options?.onResizeEnd; const resizingColumnIndex = useRef(null); const columnWidthsRef = useRef>(fields.map(_ => null)); @@ -84,21 +87,33 @@ export function useTableStyles( ); }, [fields]); - const initialTableStyles = useMemo(() => { - const gridTemplateColumns = fields.map(field => { - const staticWidth = options?.staticColumnWidths?.[field]; + const getColumnTemplateWidth = useCallback( + (field: string, index: number) => { + const resizedWidth = columnWidthsRef.current[index]; + if (typeof resizedWidth === 'number') { + return `${resizedWidth}px`; + } + const staticWidth = staticColumnWidths?.[field]; if (staticWidth) { return typeof staticWidth === 'number' ? `${staticWidth}px` : staticWidth; } return `minmax(${minimumColumnWidth}px, auto)`; - }); + }, + [minimumColumnWidth, staticColumnWidths] + ); + + const buildGridTemplateColumns = useCallback(() => { + const tracks = fields.map(getColumnTemplateWidth); if (defined(prefixColumnWidth)) { - gridTemplateColumns.unshift(prefixColumnWidth); + tracks.unshift(prefixColumnWidth); } - return { - gridTemplateColumns: gridTemplateColumns.join(' '), - }; - }, [fields, minimumColumnWidth, prefixColumnWidth, options?.staticColumnWidths]); + return tracks.join(' '); + }, [fields, prefixColumnWidth, getColumnTemplateWidth]); + + const initialTableStyles = useMemo( + () => ({gridTemplateColumns: buildGridTemplateColumns()}), + [buildGridTemplateColumns] + ); const onResizeMouseDown = useCallback( (event: React.MouseEvent, index: number) => { @@ -129,16 +144,7 @@ export function useTableStyles( columnWidthsRef.current[index] = newWidth; - // Updating the grid's `gridTemplateColumns` directly - const gridTemplateColumns = columnWidthsRef.current.map(width => { - return typeof width === 'number' - ? `${width}px` - : `minmax(${minimumColumnWidth}px, auto)`; - }); - if (defined(prefixColumnWidth)) { - gridTemplateColumns.unshift(prefixColumnWidth); - } - gridElement.style.gridTemplateColumns = gridTemplateColumns.join(' '); + gridElement.style.gridTemplateColumns = buildGridTemplateColumns(); } function onMouseUp() { @@ -147,12 +153,14 @@ export function useTableStyles( // Cleaning up event listeners window.removeEventListener('mousemove', onMouseMove); window.removeEventListener('mouseup', onMouseUp); + + onResizeEnd?.(); } window.addEventListener('mousemove', onMouseMove); window.addEventListener('mouseup', onMouseUp); }, - [tableRef, minimumColumnWidth, prefixColumnWidth] + [buildGridTemplateColumns, minimumColumnWidth, onResizeEnd, tableRef] ); return {initialTableStyles, onResizeMouseDown}; diff --git a/static/app/views/explore/logs/tables/logsInfiniteTable.tsx b/static/app/views/explore/logs/tables/logsInfiniteTable.tsx index 45e2423182bdf3..2a01f2662e5814 100644 --- a/static/app/views/explore/logs/tables/logsInfiniteTable.tsx +++ b/static/app/views/explore/logs/tables/logsInfiniteTable.tsx @@ -58,6 +58,7 @@ import { import {calculateLogsTableMinWidth} from 'sentry/views/explore/logs/tables/calculateLogsTableMinWidth'; import {LogsEmptyResults} from 'sentry/views/explore/logs/tables/logsEmptyResults'; import {LogRowContent} from 'sentry/views/explore/logs/tables/logsTableRow'; +import {useLogsTableColumnWidths} from 'sentry/views/explore/logs/tables/useLogsTableColumnWidths'; import { OurLogKnownFieldKey, type OurLogsResponseItem, @@ -239,17 +240,6 @@ export function LogsInfiniteTable({ const scrollFetchDisabled = isFunctionScrolling || autorefreshEnabled; const sharedHoverTimeoutRef = useRef(null); - const {initialTableStyles, onResizeMouseDown} = useTableStyles( - fields.slice(), - tableRef, - { - minimumColumnWidth: 50, - prefixColumnWidth: 'min-content', - staticColumnWidths: { - [OurLogKnownFieldKey.MESSAGE]: 'minmax(90px,1fr)', - }, - } - ); const estimateSize = useCallback( (index: number) => { @@ -361,6 +351,25 @@ export function LogsInfiniteTable({ const {scrollDirection, scrollOffset, isScrolling} = virtualizer; + const [staticColumnWidths, clearColumnWidths] = useLogsTableColumnWidths({ + fields, + tableRef, + isPending, + isScrolling, + dataLength: data?.length ?? 0, + }); + + const {initialTableStyles, onResizeMouseDown} = useTableStyles( + fields.slice(), + tableRef, + { + minimumColumnWidth: 50, + prefixColumnWidth: 'min-content', + staticColumnWidths, + onResizeEnd: clearColumnWidths, + } + ); + useEffect(() => { if (isFunctionScrolling && !isScrolling && scrollOffset === 0) { setTimeout(() => { diff --git a/static/app/views/explore/logs/tables/useLogsTableColumnWidths.tsx b/static/app/views/explore/logs/tables/useLogsTableColumnWidths.tsx new file mode 100644 index 00000000000000..e8cbebb4f11025 --- /dev/null +++ b/static/app/views/explore/logs/tables/useLogsTableColumnWidths.tsx @@ -0,0 +1,65 @@ +import {useCallback, useEffect, useState, type RefObject} from 'react'; + +import {useWindowSize} from 'sentry/utils/window/useWindowSize'; +import {OurLogKnownFieldKey} from 'sentry/views/explore/logs/types'; + +type ColumnWidths = Record; + +const DEFAULT_COLUMN_WIDTHS = { + [OurLogKnownFieldKey.MESSAGE]: 'minmax(90px,1fr)', +}; + +type LogsTableColumnWidthOptions = { + dataLength: number; + fields: readonly string[]; + isPending: boolean; + isScrolling: boolean; + tableRef: RefObject; +}; + +export function useLogsTableColumnWidths({ + fields, + tableRef, + isPending, + isScrolling, + dataLength, +}: LogsTableColumnWidthOptions) { + const [columnWidths, setColumnWidths] = useState(); + const windowSize = useWindowSize(); + + const clearColumnWidths = useCallback(() => setColumnWidths(undefined), []); + + useEffect(() => { + setColumnWidths(undefined); + }, [fields, windowSize]); + + useEffect(() => { + if ( + !dataLength || + !isScrolling || + !tableRef.current || + columnWidths !== undefined || + isPending + ) { + return; + } + + const domWidths = getComputedStyle(tableRef.current).gridTemplateColumns.split(/\s+/); + + setColumnWidths( + Object.fromEntries( + fields.map((field, i) => { + if (field === OurLogKnownFieldKey.MESSAGE) { + return [field, DEFAULT_COLUMN_WIDTHS[field]]; + } + if (i === fields.length - 1) { + return [field, 'minmax(0px, 1fr)']; + } + return [field, parseFloat(domWidths[i + 1]!)]; + }) + ) + ); + }, [isScrolling, isPending, dataLength, columnWidths, fields, tableRef]); + + return [columnWidths ?? DEFAULT_COLUMN_WIDTHS, clearColumnWidths] as const; +}