Skip to content

Commit

Permalink
Merge pull request #142 from KNU-HAEDAL/Design/issue-#141
Browse files Browse the repository at this point in the history
탭 컴포넌트 디자인 변경 #141
  • Loading branch information
joojjang authored Sep 22, 2024
2 parents 73b165f + 9cf419c commit 06100a7
Show file tree
Hide file tree
Showing 8 changed files with 202 additions and 269 deletions.
78 changes: 0 additions & 78 deletions src/components/common/tab/index.tsx

This file was deleted.

126 changes: 0 additions & 126 deletions src/components/common/tab/styles.ts

This file was deleted.

74 changes: 74 additions & 0 deletions src/components/common/tabs/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import {
cloneElement,
forwardRef,
ReactElement,
useEffect,
useRef,
useState,
} from 'react';

import * as S from './styles';

type TabsProps = {
selectedTab: number;
onChange: (value: number) => void;
children: ReactElement[];
};

export const Tabs = ({ selectedTab, onChange, children }: TabsProps) => {
const containerRef = useRef<HTMLDivElement>(null);
const tabRefs = useRef<(HTMLDivElement | null)[]>([]); // Tab refs 배열
const [tabWidths, setTabWidths] = useState<number[]>([]); // 각 Tab 너비 저장

// 각 Tab의 너비를 계산하여 상태로 저장
useEffect(() => {
if (tabRefs.current.length > 0) {
const widths = tabRefs.current.map(
(ref) => ref?.getBoundingClientRect().width || 0
);
setTabWidths(widths);
}
}, [children]);

const sliderWidth = tabWidths[selectedTab] - 4 || 0; // 여백 4px 빼기

const tabs = children.map((child, index) => {
const handleClick = () => {
onChange(child.props.value);
};

return cloneElement(child, {
key: child.props.value,
active: child.props.value === selectedTab,
onClick: handleClick,
ref: (el: HTMLDivElement) => (tabRefs.current[index] = el), // Tab 요소에 ref 연결
});
});

return (
<S.StyledTabs ref={containerRef}>
{tabs}
<S.TabSlider width={sliderWidth} index={selectedTab} />
</S.StyledTabs>
);
};

type TabProps = {
label: string;
value: number;
active?: boolean;
onClick?: () => void;
};

export const Tab = forwardRef<HTMLDivElement, TabProps>(
({ label, active, onClick }, ref) => {
return (
<S.StyledTab active={active} onClick={onClick} ref={ref}>
{label}
</S.StyledTab>
);
}
);

// displayName 설정으로 경고 해결
Tab.displayName = 'Tab';
77 changes: 77 additions & 0 deletions src/components/common/tabs/styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { css } from '@emotion/react';
import styled from '@emotion/styled';

export const StyledTabs = styled.div<{
position?: string;
}>`
display: flex;
justify-content: space-between; /* Tab들이 좌우로 정렬되도록 설정 */
position: relative;
align-self: center;
height: 46px;
margin: 0 16px;
padding: 4px 0;
border-radius: 10px;
background-color: var(--color-green-06);
`;

// 선택된 탭
export const TabSlider = styled.div<{
width: number;
index: number;
}>`
position: absolute;
top: 4px;
left: 4px;
width: ${({ width }) => `${width}px`};
height: 38px;
background-color: var(--color-white);
border-radius: 10px;
/* 슬라이딩 애니메이션 */
transition: 0.2s;
transform: ${({ width, index }) => `translateX(${width * index}px)`};
`;

export const StyledTab = styled.div<{
active?: boolean;
onClick?: () => void;
inactiveStyle?: React.CSSProperties;
}>`
z-index: 1;
width: 50%; /* 각 Tab의 너비를 50%로 설정하여 두 개의 Tab이 꽉 차도록 설정 */
height: 100%;
display: flex;
justify-content: center;
align-items: center;
background-color: transparent;
border: none;
font-size: var(--font-size-md);
color: var(--color-grey-02);
cursor: pointer;
${(p) =>
p.active &&
css`
color: var(--color-green-01);
font-weight: 600;
`}
`;

export const StyledTabPanels = styled.div`
height: 100%;
width: 100%;
position: relative;
text-align: center;
`;

export const StyledTabPanel = styled.div<{
active: boolean;
}>`
display: ${(p) => (p.active ? 'flex' : 'none')};
font-size: 2rem;
flex-direction: column;
width: 100%;
height: 100%;
justify-content: center;
`;
27 changes: 27 additions & 0 deletions src/components/common/tabs/tap-panels.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { ReactElement } from 'react';

import * as S from './styles';

type TapPanelsProps = {
children: ReactElement[];
};

export const TabPanels = ({ children }: TapPanelsProps) => {
return <S.StyledTabPanels>{children}</S.StyledTabPanels>;
};

type TabPanelProps = {
children?: ReactElement;
value: number;
selectedIndex: number;
};

export const TabPanel = ({ children, value, selectedIndex }: TabPanelProps) => {
const hidden: boolean = value !== selectedIndex;

return (
<S.StyledTabPanel hidden={hidden} active={!hidden}>
{children}
</S.StyledTabPanel>
);
};
Loading

0 comments on commit 06100a7

Please sign in to comment.