1+ import { ChevronDownIcon , ChevronUpDownIcon , ChevronUpIcon } from "@heroicons/react/20/solid" ;
12import type { OutputColumnMetadata } from "@internal/clickhouse" ;
3+ import { IconFilter2 } from "@tabler/icons-react" ;
24import { rankItem } from "@tanstack/match-sorter-utils" ;
35import {
4- useReactTable ,
6+ flexRender ,
57 getCoreRowModel ,
68 getFilteredRowModel ,
79 getSortedRowModel ,
8- flexRender ,
9- type ColumnDef ,
10+ useReactTable ,
1011 type CellContext ,
11- type ColumnResizeMode ,
12+ type Column ,
13+ type ColumnDef ,
1214 type ColumnFiltersState ,
15+ type ColumnResizeMode ,
1316 type FilterFn ,
14- type Column ,
15- type SortingState ,
1617 type SortDirection ,
18+ type SortingState ,
1719} from "@tanstack/react-table" ;
1820import { useVirtualizer } from "@tanstack/react-virtual" ;
1921import { formatDurationMilliseconds , MachinePresetName } from "@trigger.dev/core/v3" ;
@@ -39,12 +41,6 @@ import { Paragraph } from "../primitives/Paragraph";
3941import { TextLink } from "../primitives/TextLink" ;
4042import { InfoIconTooltip , SimpleTooltip } from "../primitives/Tooltip" ;
4143import { QueueName } from "../runs/v3/QueueName" ;
42- import {
43- FunnelIcon ,
44- ChevronUpIcon ,
45- ChevronDownIcon ,
46- ChevronUpDownIcon ,
47- } from "@heroicons/react/20/solid" ;
4844
4945const MAX_STRING_DISPLAY_LENGTH = 64 ;
5046const ROW_HEIGHT = 33 ; // Estimated row height in pixels
@@ -54,7 +50,7 @@ const MIN_COLUMN_WIDTH = 60;
5450const MAX_COLUMN_WIDTH = 400 ;
5551const CHAR_WIDTH_PX = 7.5 ; // Approximate width of a monospace character at text-xs (12px)
5652const CELL_PADDING_PX = 40 ; // px-2 (8px) on each side + buffer for copy button
57- const HEADER_ICONS_WIDTH_PX = 72 ; // Sort icon (16px) + filter icon (12px) + info icon (16px) + gaps (12px) + header padding (16px )
53+ const HEADER_ICONS_WIDTH_PX = 80 ; // Sort icon (16px) + filter icon (12px) + info icon (16px) + gaps (12px) + header padding (24px )
5854const SAMPLE_SIZE = 100 ; // Number of rows to sample for width calculation
5955
6056// Type for row data
@@ -768,6 +764,7 @@ function HeaderCellContent({
768764 sortDirection,
769765 onSortClick,
770766 canSort,
767+ isHeaderRowHovered,
771768} : {
772769 alignment : "left" | "right" ;
773770 tooltip ?: React . ReactNode ;
@@ -778,19 +775,24 @@ function HeaderCellContent({
778775 sortDirection ?: SortDirection | false ;
779776 onSortClick ?: ( event : React . MouseEvent ) => void ;
780777 canSort ?: boolean ;
778+ isHeaderRowHovered ?: boolean ;
781779} ) {
782- const [ isHovered , setIsHovered ] = useState ( false ) ;
780+ const [ isCellHovered , setIsCellHovered ] = useState ( false ) ;
781+ const [ isFilterHovered , setIsFilterHovered ] = useState ( false ) ;
782+
783+ const showIcons = isHeaderRowHovered || ! ! sortDirection || showFilters || hasActiveFilter ;
784+ const sortHighlighted = isCellHovered && ! isFilterHovered ;
783785
784786 return (
785787 < div
786788 className = { cn (
787- "flex w-full items-center gap-1 overflow-hidden bg-background-bright py-2 pl-2 pr-1 " ,
789+ "flex w-full items-center gap-1 overflow-hidden bg-background-bright py-2 pl-2 pr-3 " ,
788790 "font-mono text-xs font-medium text-text-bright" ,
789791 alignment === "right" && "justify-end" ,
790792 canSort && "cursor-pointer select-none"
791793 ) }
792- onMouseEnter = { ( ) => setIsHovered ( true ) }
793- onMouseLeave = { ( ) => setIsHovered ( false ) }
794+ onMouseEnter = { ( ) => setIsCellHovered ( true ) }
795+ onMouseLeave = { ( ) => setIsCellHovered ( false ) }
794796 onClick = { onSortClick }
795797 >
796798 { tooltip ? (
@@ -800,19 +802,25 @@ function HeaderCellContent({
800802 } ) }
801803 >
802804 < span className = "truncate text-left" > { children } </ span >
803- < InfoIconTooltip
804- content = { tooltip }
805- contentClassName = "normal-case tracking-normal"
806- enabled = { isHovered }
807- />
805+ < span className = "flex flex-shrink-0" >
806+ < InfoIconTooltip
807+ content = { tooltip }
808+ contentClassName = "normal-case tracking-normal"
809+ enabled = { isCellHovered }
810+ />
811+ </ span >
808812 </ div >
809813 ) : (
810814 < span className = "min-w-0 flex-1 truncate text-left" > { children } </ span >
811815 ) }
812816 { /* Sort indicator */ }
813817 { canSort && (
814818 < span
815- className = { cn ( "flex-shrink-0" , sortDirection ? "text-text-bright" : "text-text-dimmed" ) }
819+ className = { cn (
820+ "flex-shrink-0 transition-all" ,
821+ showIcons ? "opacity-100" : "opacity-0" ,
822+ sortHighlighted ? "text-text-bright" : "text-text-dimmed"
823+ ) }
816824 >
817825 { sortDirection === "asc" ? (
818826 < ChevronUpIcon className = "size-4" />
@@ -829,10 +837,15 @@ function HeaderCellContent({
829837 e . stopPropagation ( ) ;
830838 onFilterClick ( ) ;
831839 } }
832- className = "flex-shrink-0 rounded text-text-dimmed transition-colors hover:bg-charcoal-700 hover:text-text-bright"
840+ onMouseEnter = { ( ) => setIsFilterHovered ( true ) }
841+ onMouseLeave = { ( ) => setIsFilterHovered ( false ) }
842+ className = { cn (
843+ "flex-shrink-0 rounded text-text-dimmed transition-all hover:bg-charcoal-700 hover:text-text-bright" ,
844+ showIcons ? "opacity-100" : "opacity-0"
845+ ) }
833846 title = "Toggle column filters"
834847 >
835- < FunnelIcon className = "size-3 " />
848+ < IconFilter2 className = "size-4 " />
836849 </ button >
837850 ) }
838851 </ div >
@@ -900,6 +913,8 @@ export const TSQLResultsTable = memo(function TSQLResultsTable({
900913 const [ focusFilterColumn , setFocusFilterColumn ] = useState < string | null > ( null ) ;
901914 // State for column sorting
902915 const [ sorting , setSorting ] = useState < SortingState > ( defaultSorting ) ;
916+ // Track header row hover for showing sort/filter icons
917+ const [ isHeaderRowHovered , setIsHeaderRowHovered ] = useState ( false ) ;
903918
904919 // Create TanStack Table column definitions from OutputColumnMetadata
905920 // Calculate column widths based on content
@@ -973,6 +988,8 @@ export const TSQLResultsTable = memo(function TSQLResultsTable({
973988 top : 0 ,
974989 zIndex : 1 ,
975990 } }
991+ onMouseEnter = { ( ) => setIsHeaderRowHovered ( true ) }
992+ onMouseLeave = { ( ) => setIsHeaderRowHovered ( false ) }
976993 >
977994 { table . getHeaderGroups ( ) . map ( ( headerGroup ) => (
978995 < tr key = { headerGroup . id } style = { { display : "flex" , width : "100%" } } >
@@ -1003,6 +1020,7 @@ export const TSQLResultsTable = memo(function TSQLResultsTable({
10031020 sortDirection = { header . column . getIsSorted ( ) }
10041021 onSortClick = { header . column . getToggleSortingHandler ( ) }
10051022 canSort = { header . column . getCanSort ( ) }
1023+ isHeaderRowHovered = { isHeaderRowHovered }
10061024 >
10071025 { flexRender ( header . column . columnDef . header , header . getContext ( ) ) }
10081026 </ HeaderCellContent >
@@ -1067,6 +1085,8 @@ export const TSQLResultsTable = memo(function TSQLResultsTable({
10671085 top : 0 ,
10681086 zIndex : 1 ,
10691087 } }
1088+ onMouseEnter = { ( ) => setIsHeaderRowHovered ( true ) }
1089+ onMouseLeave = { ( ) => setIsHeaderRowHovered ( false ) }
10701090 >
10711091 { /* Main header row */ }
10721092 { table . getHeaderGroups ( ) . map ( ( headerGroup ) => (
@@ -1098,6 +1118,7 @@ export const TSQLResultsTable = memo(function TSQLResultsTable({
10981118 sortDirection = { header . column . getIsSorted ( ) }
10991119 onSortClick = { header . column . getToggleSortingHandler ( ) }
11001120 canSort = { header . column . getCanSort ( ) }
1121+ isHeaderRowHovered = { isHeaderRowHovered }
11011122 >
11021123 { flexRender ( header . column . columnDef . header , header . getContext ( ) ) }
11031124 </ HeaderCellContent >
0 commit comments