-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathTableTabs.tsx
More file actions
138 lines (125 loc) · 3.7 KB
/
TableTabs.tsx
File metadata and controls
138 lines (125 loc) · 3.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
import { ChevronLeft, ChevronRight, Table, X } from 'lucide-react'
import { type ReactNode, useEffect, useRef, useState } from 'react'
import { cn } from '../../../../shared/lib/utils'
import { Button } from '../../../../shared/ui/button'
export interface Tab {
id: string
name: string
type: 'table' | 'query'
}
interface TableTabsProps {
tabs: Tab[]
activeTab: string
onSelectTab: (id: string) => void
onCloseTab: (id: string) => void
renderTab?: (tab: Tab, defaultTab: ReactNode) => ReactNode
children: ReactNode
}
export function TableTabs({
tabs,
activeTab,
onSelectTab,
onCloseTab,
renderTab,
children
}: TableTabsProps) {
const tabsRef = useRef<HTMLDivElement>(null)
const [showScrollButtons, setShowScrollButtons] = useState(false)
useEffect(() => {
const checkScroll = () => {
if (tabsRef.current) {
setShowScrollButtons(tabsRef.current.scrollWidth > tabsRef.current.clientWidth)
}
}
checkScroll()
window.addEventListener('resize', checkScroll)
return () => window.removeEventListener('resize', checkScroll)
}, [tabs])
const scroll = (direction: 'left' | 'right') => {
if (tabsRef.current) {
const scrollAmount = 200
tabsRef.current.scrollBy({
left: direction === 'left' ? -scrollAmount : scrollAmount,
behavior: 'smooth'
})
}
}
return (
<div className="flex h-full flex-col">
<div className="border-border bg-card flex items-center border-b">
{showScrollButtons && (
<Button
variant="ghost"
size="icon"
className="border-border h-9 w-8 shrink-0 rounded-none border-r"
onClick={() => scroll('left')}
>
<ChevronLeft className="h-4 w-4" />
<span className="sr-only">Scroll left</span>
</Button>
)}
<div ref={tabsRef} className="scrollbar-none flex flex-1 overflow-x-auto">
{tabs.map(tab => {
const defaultTab = (
<button
key={tab.id}
type="button"
className={cn(
'group border-border flex shrink-0 items-center gap-2 border-r px-4 py-2 text-sm transition-colors select-none',
activeTab === tab.id
? 'bg-background text-foreground'
: 'bg-card text-muted-foreground hover:bg-secondary hover:text-foreground'
)}
onClick={() => onSelectTab(tab.id)}
>
<Table className="text-primary h-4 w-4" />
<span className="max-w-32 truncate">{tab.name}</span>
<button
type="button"
className={cn(
'ml-1 rounded p-0.5 transition-colors',
'text-muted-foreground hover:bg-secondary hover:text-foreground',
'opacity-0 group-hover:opacity-100',
activeTab === tab.id && 'opacity-100'
)}
onClick={e => {
e.stopPropagation()
onCloseTab(tab.id)
}}
>
<X className="h-3.5 w-3.5" />
</button>
</button>
)
return renderTab ? renderTab(tab, defaultTab) : defaultTab
})}
</div>
{showScrollButtons && (
<Button
variant="ghost"
size="icon"
className="border-border h-9 w-8 shrink-0 rounded-none border-l"
onClick={() => scroll('right')}
>
<ChevronRight className="h-4 w-4" />
</Button>
)}
</div>
<div className="flex-1 overflow-hidden">
{tabs.length > 0 ? (
children
) : (
<div className="text-muted-foreground flex h-full items-center justify-center">
<div className="text-center">
<Table className="text-muted-foreground/50 mx-auto h-12 w-12" />
<p className="mt-4 text-sm">Select a table to view its data</p>
<p className="text-muted-foreground mt-1 text-xs">
Use the tree on the right to browse tables
</p>
</div>
</div>
)}
</div>
</div>
)
}