11import * as React from 'react'
2- import { Key , Link } from 'lucide-react'
2+ import { Key , Link , ArrowUpDown , ArrowUpWideNarrow , ArrowDownWideNarrow } from 'lucide-react'
33import { HoverCard , HoverCardContent , HoverCardTrigger } from '@/components/ui/hover-card'
44import type { ColumnMetadata } from './DataTable'
55
6- interface ColumnInfoProps {
6+ type ColumnInfoProps = {
77 column : ColumnMetadata
88 children : React . ReactNode
99}
@@ -12,7 +12,7 @@ interface ColumnInfoProps {
1212 * Hover card component that displays detailed column metadata
1313 * Shows type, nullable status, constraints, foreign keys, etc.
1414 */
15- export function ColumnInfo ( { column, children } : ColumnInfoProps ) {
15+ function ColumnInfo ( { column, children } : ColumnInfoProps ) {
1616 return (
1717 < HoverCard openDelay = { 300 } >
1818 < HoverCardTrigger asChild > { children } </ HoverCardTrigger >
@@ -98,3 +98,68 @@ export function ColumnInfo({ column, children }: ColumnInfoProps) {
9898 </ HoverCard >
9999 )
100100}
101+
102+ type SortIconProps = {
103+ sorted : false | 'asc' | 'desc'
104+ className : string
105+ }
106+
107+ function SortIcon ( { sorted, className } : SortIconProps ) {
108+ switch ( sorted ) {
109+ case 'asc' :
110+ return < ArrowUpWideNarrow className = { className } />
111+ case 'desc' :
112+ return < ArrowDownWideNarrow className = { className } />
113+ default :
114+ return < ArrowUpDown className = { className } />
115+ }
116+ }
117+
118+ type ColumnHeaderProps = {
119+ column : ColumnMetadata
120+ sorted : false | 'asc' | 'desc'
121+ showControls ?: boolean
122+ onSortChange ?: ( column : string | null , direction : 'asc' | 'desc' | null ) => void
123+ }
124+
125+ /**
126+ * Column header component that displays column name, type, and optional sorting controls
127+ * Wraps content in a ColumnInfo hover card for displaying detailed metadata
128+ */
129+ export function ColumnHeader ( { column, sorted, showControls = true , onSortChange } : ColumnHeaderProps ) {
130+ const isKey = column . primaryKey
131+ const isForeignKey = ! ! column . foreignKey
132+ const isSortable = showControls && onSortChange
133+
134+ const handleSort = React . useCallback ( ( ) => {
135+ if ( ! onSortChange ) return
136+
137+ // Three-state sorting: no sort → asc → desc → no sort
138+ if ( ! sorted ) {
139+ onSortChange ( column . name , 'asc' )
140+ } else if ( sorted === 'asc' ) {
141+ onSortChange ( column . name , 'desc' )
142+ } else {
143+ onSortChange ( null , null )
144+ }
145+ } , [ sorted , onSortChange , column . name ] )
146+
147+ return (
148+ < ColumnInfo column = { column } >
149+ < div
150+ className = { `flex items-center gap-1.5 px-4 py-2 w-full h-full ${
151+ isSortable ? 'cursor-pointer select-none hover:text-gray-200 transition-colors' : ''
152+ } `}
153+ onClick = { isSortable ? handleSort : undefined }
154+ >
155+ { isKey && < Key className = "w-3.5 h-3.5 text-yellow-400 shrink-0" /> }
156+ { isForeignKey && < Link className = "w-3.5 h-3.5 text-blue-400 shrink-0" /> }
157+ < div className = "flex flex-col gap-0.5 flex-1 min-w-0" >
158+ < span className = "text-xs font-semibold text-gray-200 tracking-wider truncate" > { column . name } </ span >
159+ < span className = "text-[10px] text-gray-500 font-normal truncate" > { column . type } </ span >
160+ </ div >
161+ { isSortable && < SortIcon sorted = { sorted } className = "h-3.5 w-3.5 text-gray-400 shrink-0" /> }
162+ </ div >
163+ </ ColumnInfo >
164+ )
165+ }
0 commit comments