Skip to content

Commit c5d5d29

Browse files
committed
fix: improve Tabs component interaction and styling
1 parent 5918286 commit c5d5d29

File tree

1 file changed

+51
-31
lines changed

1 file changed

+51
-31
lines changed

components/Tabs.tsx

Lines changed: 51 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,68 @@
1-
import { ReactNode, useState } from 'react';
1+
import React, { ReactNode, ReactElement, useId, useState } from 'react';
22

3-
export function Tabs({ children }: { children: ReactNode }) {
4-
const tabList = Array.isArray(children) ? children : [children];
5-
const [activeTab, setActiveTab] = useState(0);
3+
type TabProps = { label: string; children: ReactNode };
64

7-
return (
8-
<div className="mb-8 w-full py-4">
9-
{/* 탭 버튼 영역 */}
10-
<div className="flex">
11-
{tabList.map((child: any, idx) => {
12-
const isActive = idx === activeTab;
13-
14-
const borderClass = isActive ? '' : '';
5+
export function Tabs({children, defaultIndex = 0, }: {
6+
children: ReactNode;
7+
defaultIndex?: number;
8+
}) {
9+
const tabList = React.Children.toArray(children) as ReactElement<TabProps>[];
10+
const [active, setActive] = useState(Math.min(defaultIndex, tabList.length - 1));
11+
const uid = useId();
1512

13+
return (
14+
<div className="w-full">
15+
{/* Tab bar */}
16+
<div role="tablist" aria-label="Tabs" className="relative flex items-end w-full">
17+
{tabList.map((tab, i) => {
18+
const isActive = i === active;
1619
return (
17-
<div
18-
key={idx}
19-
onClick={() => setActiveTab(idx)}
20-
tabIndex={0}
21-
className={`px-4 py-2 text-mi cursor-pointer transition-all rounded-t border
22-
${borderClass}
23-
${
24-
isActive
25-
? 'bg-blue-10 border-b-2 border-b-blue-500 text-blue-600 font-semibold'
26-
: 'border-b border-gray-200 text-gray-500 hover:bg-gray-100 hover:text-black'
27-
}
28-
`}
20+
<button
21+
key={i}
22+
role="tab"
23+
id={`${uid}-tab-${i}`}
24+
aria-selected={isActive}
25+
aria-controls={`${uid}-panel-${i}`}
26+
onClick={() => setActive(i)}
27+
className={[
28+
'relative -mb-px px-4 py-2 text-sm md:text-base select-none outline-none transition border',
29+
'first:rounded-tl-lg last:rounded-tr-lg',
30+
i > 0 ? '-ml-px' : '',
31+
isActive
32+
? [
33+
// 기존 스타일 유지
34+
'bg-white text-blue-600 border-gray-300 border-b-0 font-semibold z-20 relative scale-105',
35+
// 활성 탭이 "첫 번째"일 때 왼쪽 경계선을 재도색
36+
"first:before:content-[''] first:before:absolute first:before:top-0 first:before:-left-px first:before:h-full first:before:w-px first:before:bg-gray-300 first:dark:before:bg-gray-700",
37+
// 스케일 기준점을 왼쪽-아래로 둬서 왼쪽 선이 밀리지 않게 함
38+
'origin-bottom-left',
39+
].join(' ')
40+
: 'bg-white text-gray-500 hover:text-gray-700 hover:bg-gray-50 border-gray-300 font-normal'
41+
42+
].join(' ')}
2943
>
30-
{child.props.label}
31-
</div>
44+
{tab.props.label}
45+
</button>
3246
);
3347
})}
48+
{/* 오른쪽 빈 공간 라인 맞추기 */}
49+
<div className="flex-1 h-px bg-gray-300 dark:bg-gray-700" />
3450
</div>
3551

36-
{/* 콘텐츠 영역 */}
52+
{/* Panel */}
3753
<div
38-
className="space-x-2 px-6 border-t shadow-sm border border-gray-200 bg-white rounded-t">
39-
{tabList[activeTab]}
54+
role="tabpanel"
55+
id={`${uid}-panel-${active}`}
56+
aria-labelledby={`${uid}-tab-${active}`}
57+
className="rounded-b-lg border border-gray-300 border-t-0 bg-white p-5 md:p-7 shadow-sm
58+
dark:border-gray-700 dark:bg-gray-900"
59+
>
60+
{tabList[active]}
4061
</div>
4162
</div>
4263
);
4364
}
4465

45-
export function Tab({children}: { children: ReactNode }) {
66+
export function Tab({ children }: TabProps) {
4667
return <div>{children}</div>;
4768
}
48-

0 commit comments

Comments
 (0)