From 1f932866737de33e8f894ae5c8fbd695c09bda27 Mon Sep 17 00:00:00 2001 From: joojjang Date: Sat, 21 Sep 2024 19:16:48 +0900 Subject: [PATCH 01/10] =?UTF-8?q?design(tab):=20=ED=83=AD=20=EB=B0=B0?= =?UTF-8?q?=EA=B2=BD=EC=83=89=20=EB=B3=80=EA=B2=BD=20=EB=B0=8F=20=EA=B7=B8?= =?UTF-8?q?=EB=A6=BC=EC=9E=90=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/common/tab/styles.ts | 39 +++-------------------------- 1 file changed, 4 insertions(+), 35 deletions(-) diff --git a/src/components/common/tab/styles.ts b/src/components/common/tab/styles.ts index d542d83..07d9c62 100644 --- a/src/components/common/tab/styles.ts +++ b/src/components/common/tab/styles.ts @@ -1,8 +1,6 @@ -import { css, keyframes } from '@emotion/react'; +import { css } from '@emotion/react'; import styled from '@emotion/styled'; -// 챌린지 상세 페이지 css - export const TabsContainer = styled.div` display: flex; position: relative; @@ -23,23 +21,6 @@ export const TabPanelContainer = styled.div` text-align: center; `; -export const Image = styled.img` - position: relative; - margin: auto; - display: block; - height: 40%; - opacity: 20%; - object-fit: cover; - margin-bottom: 28px; - filter: grayscale(100%); -`; - -export const ImageMask = styled.div` - background-color: var(--color-green-06); - position: relative; - top: 50px; -`; - export const Wrapper = styled.div``; type StylizedTabProps = { @@ -63,10 +44,9 @@ export const StylizedTab = styled.div` ${(p) => p.active && css` - color: var(--color-white); - font-weight: bold; + color: var(--color-green-01); + font-weight: 600; border-radius: 20px; - animation: ${inset} 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94) both; `} `; @@ -107,20 +87,9 @@ export const TabSlider = styled.div` position: absolute; top: 3px; height: 50px; - background-color: var(--color-green-01); + background-color: var(--color-white); border-radius: 20px; transition: 0.2s; transform: ${({ width, index }) => `translateX(${width * index}px)`}; width: ${({ width }) => `${width}px`}; `; - -const inset = keyframes` - 0% { - -webkit-box-shadow: 0 0 0 0 rgba(92, 198, 186, 0); - box-shadow: 0 0 0 0 rgba(92, 198, 186, 0); - } - 100% { - -webkit-box-shadow: 3px 3px 3px rgba(92, 198, 186, 0.5); - box-shadow: 3px 3px 3px rgba(92, 198, 186, 0.5); - } - `; From a4638f3179d889982b3497f54d77e790cc5b0e71 Mon Sep 17 00:00:00 2001 From: joojjang Date: Sat, 21 Sep 2024 19:29:01 +0900 Subject: [PATCH 02/10] =?UTF-8?q?style(tab):=20props=20=ED=83=80=EC=9E=85?= =?UTF-8?q?=20=EC=9D=B8=EB=9D=BC=EC=9D=B8=EC=9C=BC=EB=A1=9C=20=EC=A0=95?= =?UTF-8?q?=EC=9D=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/common/tab/styles.ts | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/src/components/common/tab/styles.ts b/src/components/common/tab/styles.ts index 07d9c62..5f854b2 100644 --- a/src/components/common/tab/styles.ts +++ b/src/components/common/tab/styles.ts @@ -21,15 +21,11 @@ export const TabPanelContainer = styled.div` text-align: center; `; -export const Wrapper = styled.div``; - -type StylizedTabProps = { +export const StylizedTab = styled.div<{ active?: boolean; onClick?: () => void; inactiveStyle?: React.CSSProperties; -}; - -export const StylizedTab = styled.div` +}>` z-index: 1; color: var(--color-grey-02); width: 50%; /* 각 Tab의 너비를 50%로 설정하여 두 개의 Tab이 꽉 차도록 설정 */ @@ -50,11 +46,9 @@ export const StylizedTab = styled.div` `} `; -type StyledTabPanelProps = { +export const StyledTabPanel = styled.div<{ active: boolean; -}; - -export const StyledTabPanel = styled.div` +}>` display: ${(p) => (p.active ? 'flex' : 'none')}; font-size: 2rem; flex-direction: column; @@ -63,11 +57,9 @@ export const StyledTabPanel = styled.div` justify-content: center; `; -type TabHeaderContainerProps = { +export const TabHeaderContainer = styled.div<{ position?: string; -}; - -export const TabHeaderContainer = styled.div` +}>` position: ${(props) => props.position || 'absolute'}; width: 100%; `; @@ -78,17 +70,18 @@ export const TabsHolder = styled.div` width: 100%; `; -type TabSliderProps = { +// 선택된 탭 +export const TabSlider = styled.div<{ width: number; index: number; -}; - -export const TabSlider = styled.div` +}>` position: absolute; top: 3px; height: 50px; background-color: var(--color-white); border-radius: 20px; + + /* 슬라이딩 애니메이션 */ transition: 0.2s; transform: ${({ width, index }) => `translateX(${width * index}px)`}; width: ${({ width }) => `${width}px`}; From 3a4c13a30fb501d3c3a799f990d0bbdfebaa8116 Mon Sep 17 00:00:00 2001 From: joojjang Date: Sun, 22 Sep 2024 16:31:30 +0900 Subject: [PATCH 03/10] =?UTF-8?q?design(tab):=20=ED=83=AD=20=EB=94=94?= =?UTF-8?q?=EC=9E=90=EC=9D=B8=20=EB=B3=80=EA=B2=BD=20=EC=A4=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/common/tab/styles.ts | 11 ++++++----- src/pages/challenge-detail/styles.ts | 4 ++-- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/components/common/tab/styles.ts b/src/components/common/tab/styles.ts index 5f854b2..6961a1d 100644 --- a/src/components/common/tab/styles.ts +++ b/src/components/common/tab/styles.ts @@ -32,7 +32,7 @@ export const StylizedTab = styled.div<{ font-size: var(--font-size-md); background-color: transparent; border: none; - height: 50px; + height: 38px; text-align: center; line-height: 50px; cursor: pointer; @@ -42,7 +42,7 @@ export const StylizedTab = styled.div<{ css` color: var(--color-green-01); font-weight: 600; - border-radius: 20px; + border-radius: 10px; `} `; @@ -76,10 +76,11 @@ export const TabSlider = styled.div<{ index: number; }>` position: absolute; - top: 3px; - height: 50px; + top: 4px; + height: 38px; background-color: var(--color-white); - border-radius: 20px; + border-radius: 10px; + margin: 0 4px; /* 슬라이딩 애니메이션 */ transition: 0.2s; diff --git a/src/pages/challenge-detail/styles.ts b/src/pages/challenge-detail/styles.ts index 7365586..2171f30 100644 --- a/src/pages/challenge-detail/styles.ts +++ b/src/pages/challenge-detail/styles.ts @@ -5,8 +5,8 @@ export const TabsContainer = styled.div` position: relative; align-self: center; margin: 0 16px; - height: 55px; - border-radius: 20px; + height: 46px; + border-radius: 10px; background-color: var(--color-green-06); `; From 144c75ed49ffd143f03dbc1a3ac6aa219b2a490a Mon Sep 17 00:00:00 2001 From: joojjang Date: Sun, 22 Sep 2024 16:33:47 +0900 Subject: [PATCH 04/10] =?UTF-8?q?remove(tab):=20=ED=95=84=EC=9A=94=20?= =?UTF-8?q?=EC=97=86=EB=8A=94=20=EC=BD=94=EB=93=9C=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/common/tab/styles.ts | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/src/components/common/tab/styles.ts b/src/components/common/tab/styles.ts index 6961a1d..5fc01be 100644 --- a/src/components/common/tab/styles.ts +++ b/src/components/common/tab/styles.ts @@ -1,26 +1,6 @@ import { css } from '@emotion/react'; import styled from '@emotion/styled'; -export const TabsContainer = styled.div` - display: flex; - position: relative; - top: 50px; - align-self: center; - width: 90%; - margin: 0 auto; - height: 55px; - border-radius: 20px; - background-color: var(--color-green-06); -`; - -export const TabPanelContainer = styled.div` - height: 100%; - width: 100%; - position: relative; - top: 50px; - text-align: center; -`; - export const StylizedTab = styled.div<{ active?: boolean; onClick?: () => void; From 07c3303f6b93438fac902f5e2c3079aceab5678c Mon Sep 17 00:00:00 2001 From: joojjang Date: Sun, 22 Sep 2024 17:00:28 +0900 Subject: [PATCH 05/10] =?UTF-8?q?refactor(tab):=20challenge-detail?= =?UTF-8?q?=EA=B3=BC=20tab=20=EA=B0=81=EA=B0=81=20=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EB=B6=84?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/common/tab/index.tsx | 54 ++++++++++++++++------------ src/components/common/tab/styles.ts | 42 ++++++++++++++-------- src/pages/challenge-detail/index.tsx | 27 +++++++------- src/pages/challenge-detail/styles.ts | 19 +--------- 4 files changed, 73 insertions(+), 69 deletions(-) diff --git a/src/components/common/tab/index.tsx b/src/components/common/tab/index.tsx index 4dc8a37..2a1d8d1 100644 --- a/src/components/common/tab/index.tsx +++ b/src/components/common/tab/index.tsx @@ -2,13 +2,6 @@ import { cloneElement, ReactElement, useEffect, useRef, useState } from 'react'; import * as S from './styles'; -type TabProps = { - label: string; - value: number; - active?: boolean; - onClick?: () => void; -}; - type TabsProps = { selectedTab: number; onChange: (value: number) => void; @@ -16,20 +9,6 @@ type TabsProps = { position?: string; }; -type TabPanelProps = { - children?: ReactElement; - value: number; - selectedIndex: number; -}; - -export const Tab = ({ label, active, onClick }: TabProps) => { - return ( - - {label} - - ); -}; - export const Tabs = ({ selectedTab, onChange, @@ -60,13 +39,42 @@ export const Tabs = ({ }); return ( - + {tabs} - + ); }; +type TabProps = { + label: string; + value: number; + active?: boolean; + onClick?: () => void; +}; + +export const Tab = ({ label, active, onClick }: TabProps) => { + return ( + + {label} + + ); +}; + +type TapPanelsProps = { + children: ReactElement[]; +}; + +export const TabPanels = ({ children }: TapPanelsProps) => { + return {children}; +}; + +type TabPanelProps = { + children?: ReactElement; + value: number; + selectedIndex: number; +}; + export const TabPanel = ({ children, value, selectedIndex }: TabPanelProps) => { const hidden: boolean = value !== selectedIndex; diff --git a/src/components/common/tab/styles.ts b/src/components/common/tab/styles.ts index 5fc01be..341bf28 100644 --- a/src/components/common/tab/styles.ts +++ b/src/components/common/tab/styles.ts @@ -1,7 +1,7 @@ import { css } from '@emotion/react'; import styled from '@emotion/styled'; -export const StylizedTab = styled.div<{ +export const StyledTab = styled.div<{ active?: boolean; onClick?: () => void; inactiveStyle?: React.CSSProperties; @@ -26,22 +26,18 @@ export const StylizedTab = styled.div<{ `} `; -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; -`; - -export const TabHeaderContainer = styled.div<{ +export const StyledTabs = styled.div<{ position?: string; }>` position: ${(props) => props.position || 'absolute'}; - width: 100%; + + display: flex; + position: relative; + align-self: center; + margin: 0 16px; + height: 46px; + border-radius: 10px; + background-color: var(--color-green-06); `; export const TabsHolder = styled.div` @@ -67,3 +63,21 @@ export const TabSlider = styled.div<{ transform: ${({ width, index }) => `translateX(${width * index}px)`}; width: ${({ width }) => `${width}px`}; `; + +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; +`; diff --git a/src/pages/challenge-detail/index.tsx b/src/pages/challenge-detail/index.tsx index cd0d99d..23cac53 100644 --- a/src/pages/challenge-detail/index.tsx +++ b/src/pages/challenge-detail/index.tsx @@ -8,7 +8,7 @@ import { ReviewSection } from './review-section/'; import * as S from './styles'; import { type ChallengeDetailData } from '@/apis/challenge-detail/challenge.detail.response'; import DefaultImage from '@/assets/Default-Image.svg'; -import { Tab, TabPanel, Tabs } from '@/components/common/tab'; +import { Tabs, Tab, TabPanels, TabPanel } from '@/components/common/tab'; import TopBar from '@/components/features/layout/top-bar'; // const CHALLENGE_GROUP_ID = 38; @@ -27,15 +27,15 @@ const ChallengeDetailPage = () => { const tabsList = [ { label: '설명', - component: data ? : null, + panel: data ? : null, }, { label: '랭킹', - component: data ? : null, + panel: data ? : null, }, { label: '리뷰', - component: data ? : null, + panel: data ? : null, }, ]; @@ -78,20 +78,19 @@ const ChallengeDetailPage = () => { {data?.category} {data?.title} - - - {tabsList.map((t, index) => ( - - ))} - - - + + + {tabsList.map((t, index) => ( + + ))} + + {tabsList.map((t, index) => ( - {t.component ?? undefined} + {t.panel ?? undefined} ))} - + ); }; diff --git a/src/pages/challenge-detail/styles.ts b/src/pages/challenge-detail/styles.ts index 2171f30..1f7c8fa 100644 --- a/src/pages/challenge-detail/styles.ts +++ b/src/pages/challenge-detail/styles.ts @@ -1,21 +1,6 @@ import styled from '@emotion/styled'; -export const TabsContainer = styled.div` - display: flex; - position: relative; - align-self: center; - margin: 0 16px; - height: 46px; - border-radius: 10px; - background-color: var(--color-green-06); -`; - -export const TabPanelContainer = styled.div` - height: 100%; - width: 100%; - position: relative; - text-align: center; -`; +export const Wrapper = styled.div``; export const ImageMask = styled.div` background-color: var(--color-green-06); @@ -33,8 +18,6 @@ export const Image = styled.img` filter: grayscale(100%); `; -export const Wrapper = styled.div``; - export const ChallengeTitleWrapper = styled.div` margin: 16px; display: flex; From f0257426aea3f20527509e6d6fdfe5d5c6972315 Mon Sep 17 00:00:00 2001 From: joojjang Date: Sun, 22 Sep 2024 17:01:31 +0900 Subject: [PATCH 06/10] =?UTF-8?q?fix(challenge-detail):=20useEffect=20?= =?UTF-8?q?=EC=9D=98=EC=A1=B4=EC=84=B1=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/challenge-detail/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/challenge-detail/index.tsx b/src/pages/challenge-detail/index.tsx index 23cac53..97e57f0 100644 --- a/src/pages/challenge-detail/index.tsx +++ b/src/pages/challenge-detail/index.tsx @@ -55,7 +55,7 @@ const ChallengeDetailPage = () => { }; fetchChallengeDetail(); - }, []); + }, [challengeGroupId]); // 챌린지 리뷰 페이지에 필요한 챌린지 제목 세션 스토리지에 저장 useEffect(() => { From 5278816090e42d58c221655195ca3abdcf5377af Mon Sep 17 00:00:00 2001 From: joojjang Date: Sun, 22 Sep 2024 17:40:48 +0900 Subject: [PATCH 07/10] =?UTF-8?q?refactor(tabs):=20tabs,=20tab-panels?= =?UTF-8?q?=EB=A1=9C=20=EB=AA=A8=EB=93=88=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/common/{tab => tabs}/index.tsx | 24 ----------- src/components/common/{tab => tabs}/styles.ts | 0 src/components/common/tabs/tap-panels.tsx | 27 +++++++++++++ src/pages/challenge-detail/index.tsx | 3 +- src/pages/challenge-record/index.tsx | 40 ++++--------------- 5 files changed, 37 insertions(+), 57 deletions(-) rename src/components/common/{tab => tabs}/index.tsx (72%) rename src/components/common/{tab => tabs}/styles.ts (100%) create mode 100644 src/components/common/tabs/tap-panels.tsx diff --git a/src/components/common/tab/index.tsx b/src/components/common/tabs/index.tsx similarity index 72% rename from src/components/common/tab/index.tsx rename to src/components/common/tabs/index.tsx index 2a1d8d1..dc25a42 100644 --- a/src/components/common/tab/index.tsx +++ b/src/components/common/tabs/index.tsx @@ -60,27 +60,3 @@ export const Tab = ({ label, active, onClick }: TabProps) => { ); }; - -type TapPanelsProps = { - children: ReactElement[]; -}; - -export const TabPanels = ({ children }: TapPanelsProps) => { - return {children}; -}; - -type TabPanelProps = { - children?: ReactElement; - value: number; - selectedIndex: number; -}; - -export const TabPanel = ({ children, value, selectedIndex }: TabPanelProps) => { - const hidden: boolean = value !== selectedIndex; - - return ( - - ); -}; diff --git a/src/components/common/tab/styles.ts b/src/components/common/tabs/styles.ts similarity index 100% rename from src/components/common/tab/styles.ts rename to src/components/common/tabs/styles.ts diff --git a/src/components/common/tabs/tap-panels.tsx b/src/components/common/tabs/tap-panels.tsx new file mode 100644 index 0000000..6d25cd4 --- /dev/null +++ b/src/components/common/tabs/tap-panels.tsx @@ -0,0 +1,27 @@ +import { ReactElement } from 'react'; + +import * as S from './styles'; + +type TapPanelsProps = { + children: ReactElement[]; +}; + +export const TabPanels = ({ children }: TapPanelsProps) => { + return {children}; +}; + +type TabPanelProps = { + children?: ReactElement; + value: number; + selectedIndex: number; +}; + +export const TabPanel = ({ children, value, selectedIndex }: TabPanelProps) => { + const hidden: boolean = value !== selectedIndex; + + return ( + + ); +}; diff --git a/src/pages/challenge-detail/index.tsx b/src/pages/challenge-detail/index.tsx index 97e57f0..346baaf 100644 --- a/src/pages/challenge-detail/index.tsx +++ b/src/pages/challenge-detail/index.tsx @@ -8,7 +8,8 @@ import { ReviewSection } from './review-section/'; import * as S from './styles'; import { type ChallengeDetailData } from '@/apis/challenge-detail/challenge.detail.response'; import DefaultImage from '@/assets/Default-Image.svg'; -import { Tabs, Tab, TabPanels, TabPanel } from '@/components/common/tab'; +import { Tabs, Tab } from '@/components/common/tabs'; +import { TabPanels, TabPanel } from '@/components/common/tabs/tap-panels'; import TopBar from '@/components/features/layout/top-bar'; // const CHALLENGE_GROUP_ID = 38; diff --git a/src/pages/challenge-record/index.tsx b/src/pages/challenge-record/index.tsx index 0bcf751..99d2035 100644 --- a/src/pages/challenge-record/index.tsx +++ b/src/pages/challenge-record/index.tsx @@ -2,14 +2,11 @@ import { useState } from 'react'; import StampBoard from './components/stamp-board'; import Verification from './components/verification'; -import { Tab, TabPanel, Tabs } from '@/components/common/tab'; +import { Tabs, Tab } from '@/components/common/tabs'; +import { TabPanels, TabPanel } from '@/components/common/tabs/tap-panels'; import TopBar from '@/components/features/layout/top-bar'; import styled from '@emotion/styled'; -type TabsContainerProps = { - position?: string; -}; - const ChallengeRecord = () => { const [activeTab, setActiveTab] = useState<0 | 1>(0); @@ -21,20 +18,18 @@ const ChallengeRecord = () => { <> - - handleTab(value)}> - - - - - + handleTab(value)}> + + + + - + ); @@ -49,22 +44,3 @@ const ChallengeRecordLayout = styled.div` height: 100%; margin: 0 1.5rem; `; - -const TabsContainer = styled.div` - display: flex; - align-self: center; - width: 100%; - margin: 1rem auto; - height: 55px; - border-radius: 20px; - background-color: var(--color-green-06); -`; - -const TabPanelContainer = styled.div` - display: flex; - flex-direction: column; - align-items: center; - height: 100%; - width: 100%; - text-align: center; -`; From bb5d7f5bc24f8edcf291723d032b6664d7678a2d Mon Sep 17 00:00:00 2001 From: joojjang Date: Sun, 22 Sep 2024 17:45:24 +0900 Subject: [PATCH 08/10] =?UTF-8?q?style(tab):=20css=20=ED=8C=8C=EC=9D=BC=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EC=88=9C=EC=84=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/common/tabs/styles.ts | 49 ++++++++++++++-------------- 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/src/components/common/tabs/styles.ts b/src/components/common/tabs/styles.ts index 341bf28..6d55cbc 100644 --- a/src/components/common/tabs/styles.ts +++ b/src/components/common/tabs/styles.ts @@ -1,31 +1,6 @@ import { css } from '@emotion/react'; import styled from '@emotion/styled'; -export const StyledTab = styled.div<{ - active?: boolean; - onClick?: () => void; - inactiveStyle?: React.CSSProperties; -}>` - z-index: 1; - color: var(--color-grey-02); - width: 50%; /* 각 Tab의 너비를 50%로 설정하여 두 개의 Tab이 꽉 차도록 설정 */ - font-size: var(--font-size-md); - background-color: transparent; - border: none; - height: 38px; - text-align: center; - line-height: 50px; - cursor: pointer; - - ${(p) => - p.active && - css` - color: var(--color-green-01); - font-weight: 600; - border-radius: 10px; - `} -`; - export const StyledTabs = styled.div<{ position?: string; }>` @@ -64,6 +39,30 @@ export const TabSlider = styled.div<{ width: ${({ width }) => `${width}px`}; `; +export const StyledTab = styled.div<{ + active?: boolean; + onClick?: () => void; + inactiveStyle?: React.CSSProperties; +}>` + z-index: 1; + color: var(--color-grey-02); + width: 50%; /* 각 Tab의 너비를 50%로 설정하여 두 개의 Tab이 꽉 차도록 설정 */ + font-size: var(--font-size-md); + background-color: transparent; + border: none; + height: 38px; + text-align: center; + line-height: 50px; + cursor: pointer; + + ${(p) => + p.active && + css` + color: var(--color-green-01); + font-weight: 600; + `} +`; + export const StyledTabPanels = styled.div` height: 100%; width: 100%; From 72772703c41ad10208954d21b464f8c5c6fe49b1 Mon Sep 17 00:00:00 2001 From: joojjang Date: Sun, 22 Sep 2024 20:24:41 +0900 Subject: [PATCH 09/10] =?UTF-8?q?refactor(tab):=20=ED=95=84=EC=9A=94=20?= =?UTF-8?q?=EC=97=86=EB=8A=94=20=EB=B6=80=EB=B6=84=20=EC=82=AD=EC=A0=9C,?= =?UTF-8?q?=20=EC=A0=9C=20=EC=97=AD=ED=95=A0=EC=97=90=20=EB=A7=9E=EA=B2=8C?= =?UTF-8?q?=20=EA=B8=B0=EB=8A=A5=ED=95=98=EB=8F=84=EB=A1=9D=20=EB=B0=B0?= =?UTF-8?q?=EC=B9=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/common/tabs/index.tsx | 12 +++--------- src/components/common/tabs/styles.ts | 26 ++++++++++---------------- 2 files changed, 13 insertions(+), 25 deletions(-) diff --git a/src/components/common/tabs/index.tsx b/src/components/common/tabs/index.tsx index dc25a42..5d62598 100644 --- a/src/components/common/tabs/index.tsx +++ b/src/components/common/tabs/index.tsx @@ -6,15 +6,9 @@ type TabsProps = { selectedTab: number; onChange: (value: number) => void; children: ReactElement[]; - position?: string; }; -export const Tabs = ({ - selectedTab, - onChange, - children, - position, -}: TabsProps) => { +export const Tabs = ({ selectedTab, onChange, children }: TabsProps) => { const containerRef = useRef(null); const [containerWidth, setContainerWidth] = useState(0); @@ -39,8 +33,8 @@ export const Tabs = ({ }); return ( - - {tabs} + + {tabs} ); diff --git a/src/components/common/tabs/styles.ts b/src/components/common/tabs/styles.ts index 6d55cbc..4086884 100644 --- a/src/components/common/tabs/styles.ts +++ b/src/components/common/tabs/styles.ts @@ -4,23 +4,17 @@ import styled from '@emotion/styled'; export const StyledTabs = styled.div<{ position?: string; }>` - position: ${(props) => props.position || 'absolute'}; - display: flex; + justify-content: space-between; /* Tab들이 좌우로 정렬되도록 설정 */ position: relative; align-self: center; - margin: 0 16px; height: 46px; + margin: 0 16px; + padding: 4px 0; border-radius: 10px; background-color: var(--color-green-06); `; -export const TabsHolder = styled.div` - display: flex; - justify-content: space-between; /* Tab들이 좌우로 정렬되도록 설정 */ - width: 100%; -`; - // 선택된 탭 export const TabSlider = styled.div<{ width: number; @@ -28,15 +22,14 @@ export const TabSlider = styled.div<{ }>` position: absolute; top: 4px; + width: ${({ width }) => `${width}px`}; height: 38px; background-color: var(--color-white); border-radius: 10px; - margin: 0 4px; /* 슬라이딩 애니메이션 */ transition: 0.2s; transform: ${({ width, index }) => `translateX(${width * index}px)`}; - width: ${({ width }) => `${width}px`}; `; export const StyledTab = styled.div<{ @@ -45,14 +38,15 @@ export const StyledTab = styled.div<{ inactiveStyle?: React.CSSProperties; }>` z-index: 1; - color: var(--color-grey-02); width: 50%; /* 각 Tab의 너비를 50%로 설정하여 두 개의 Tab이 꽉 차도록 설정 */ - font-size: var(--font-size-md); + height: 100%; + display: flex; + justify-content: center; + align-items: center; background-color: transparent; border: none; - height: 38px; - text-align: center; - line-height: 50px; + font-size: var(--font-size-md); + color: var(--color-grey-02); cursor: pointer; ${(p) => From 9cf419c7ba66b5c3f0675ac78be32fd3b3e5c521 Mon Sep 17 00:00:00 2001 From: joojjang Date: Sun, 22 Sep 2024 20:36:36 +0900 Subject: [PATCH 10/10] =?UTF-8?q?fix(TabSlider):=20=EB=84=88=EB=B9=84=20?= =?UTF-8?q?=EC=9E=AC=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/common/tabs/index.tsx | 46 +++++++++++++++++++--------- src/components/common/tabs/styles.ts | 1 + 2 files changed, 33 insertions(+), 14 deletions(-) diff --git a/src/components/common/tabs/index.tsx b/src/components/common/tabs/index.tsx index 5d62598..7f1160d 100644 --- a/src/components/common/tabs/index.tsx +++ b/src/components/common/tabs/index.tsx @@ -1,4 +1,11 @@ -import { cloneElement, ReactElement, useEffect, useRef, useState } from 'react'; +import { + cloneElement, + forwardRef, + ReactElement, + useEffect, + useRef, + useState, +} from 'react'; import * as S from './styles'; @@ -10,17 +17,22 @@ type TabsProps = { export const Tabs = ({ selectedTab, onChange, children }: TabsProps) => { const containerRef = useRef(null); - const [containerWidth, setContainerWidth] = useState(0); + const tabRefs = useRef<(HTMLDivElement | null)[]>([]); // Tab refs 배열 + const [tabWidths, setTabWidths] = useState([]); // 각 Tab 너비 저장 + // 각 Tab의 너비를 계산하여 상태로 저장 useEffect(() => { - if (containerRef.current) { - setContainerWidth(containerRef.current.getBoundingClientRect().width); + if (tabRefs.current.length > 0) { + const widths = tabRefs.current.map( + (ref) => ref?.getBoundingClientRect().width || 0 + ); + setTabWidths(widths); } - }, [containerRef]); + }, [children]); - const sliderWidth = containerWidth / children.length; + const sliderWidth = tabWidths[selectedTab] - 4 || 0; // 여백 4px 빼기 - const tabs = children.map((child) => { + const tabs = children.map((child, index) => { const handleClick = () => { onChange(child.props.value); }; @@ -29,6 +41,7 @@ export const Tabs = ({ selectedTab, onChange, children }: TabsProps) => { key: child.props.value, active: child.props.value === selectedTab, onClick: handleClick, + ref: (el: HTMLDivElement) => (tabRefs.current[index] = el), // Tab 요소에 ref 연결 }); }); @@ -47,10 +60,15 @@ type TabProps = { onClick?: () => void; }; -export const Tab = ({ label, active, onClick }: TabProps) => { - return ( - - {label} - - ); -}; +export const Tab = forwardRef( + ({ label, active, onClick }, ref) => { + return ( + + {label} + + ); + } +); + +// displayName 설정으로 경고 해결 +Tab.displayName = 'Tab'; diff --git a/src/components/common/tabs/styles.ts b/src/components/common/tabs/styles.ts index 4086884..993b914 100644 --- a/src/components/common/tabs/styles.ts +++ b/src/components/common/tabs/styles.ts @@ -22,6 +22,7 @@ export const TabSlider = styled.div<{ }>` position: absolute; top: 4px; + left: 4px; width: ${({ width }) => `${width}px`}; height: 38px; background-color: var(--color-white);