Skip to content

Commit 2699906

Browse files
authored
Merge pull request #140 from oodd-team/feat/OD-201
[OD-201] 스켈레톤 UI 컴포넌트화 및 Profile/Account/Post 적용
2 parents ddc77ee + a0a79f6 commit 2699906

11 files changed

Lines changed: 305 additions & 47 deletions

File tree

src/components/Skeleton/index.tsx

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { SkeletonContainer } from './styles';
2+
3+
interface SkeletonProps {
4+
width?: string | number;
5+
height?: string | number;
6+
borderRadius?: string | number;
7+
className?: string;
8+
style?: React.CSSProperties;
9+
}
10+
11+
const Skeleton: React.FC<SkeletonProps> = ({
12+
width = '100%',
13+
height = '16px',
14+
borderRadius = '5px',
15+
className = '',
16+
}) => {
17+
// width와 height가 숫자인 경우 rem 단위를 추가
18+
const getSize = (size: string | number) => {
19+
if (typeof size === 'number') {
20+
return `${size}rem`;
21+
}
22+
return size;
23+
};
24+
25+
return (
26+
<SkeletonContainer
27+
className={className}
28+
style={{
29+
width: getSize(width),
30+
height: getSize(height),
31+
borderRadius: getSize(borderRadius),
32+
}}
33+
/>
34+
);
35+
};
36+
37+
export default Skeleton;

src/components/Skeleton/styles.tsx

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { styled } from 'styled-components';
2+
3+
export const SkeletonContainer = styled.div`
4+
background-color: #e0e0e0;
5+
position: relative;
6+
overflow: hidden;
7+
background: linear-gradient(90deg, #e0e0e0 0%, #f0f0f0 50%, #e0e0e0 100%);
8+
background-size: 200% 100%;
9+
animation: shimmer 1.5s infinite;
10+
11+
@keyframes shimmer {
12+
0% {
13+
background-position: 200% 0;
14+
}
15+
100% {
16+
background-position: -200% 0;
17+
}
18+
}
19+
`;

src/pages/Account/AccountCancel/index.tsx

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useState } from 'react';
1+
import { useState, useEffect } from 'react';
22
import { useNavigate } from 'react-router-dom';
33

44
import theme from '@styles/theme';
@@ -11,6 +11,7 @@ import back from '@assets/arrow/left.svg';
1111
import BottomButton from '@components/BottomButton/index';
1212
import { OODDFrame } from '@components/Frame/Frame';
1313
import Modal from '@components/Modal/index';
14+
import Skeleton from '@components/Skeleton';
1415
import { StyledText } from '@components/Text/StyledText';
1516
import TopBar from '@components/TopBar/index';
1617

@@ -30,8 +31,15 @@ const AccountCancel: React.FC = () => {
3031
const [isChecked, setIsChecked] = useState(false);
3132
const [modalContent, setModalContent] = useState<string | null>(null);
3233
const [isModalVisible, setIsModalVisible] = useState<boolean>(false);
34+
const [isLoading, setIsLoading] = useState(true); // Loading state
3335
const navigate = useNavigate();
3436

37+
useEffect(() => {
38+
setTimeout(() => {
39+
setIsLoading(false);
40+
}, 1000);
41+
}, []);
42+
3543
const handleCheckboxChange = () => {
3644
setIsChecked(!isChecked);
3745
};
@@ -79,6 +87,23 @@ const AccountCancel: React.FC = () => {
7987
}
8088
};
8189

90+
if (isLoading) {
91+
return (
92+
<OODDFrame>
93+
<CancelContainer>
94+
<TopBar text="회원 탈퇴" LeftButtonSrc={back} onClickLeftButton={() => navigate(-1)} />
95+
<SubTitle>
96+
<StyledText as="div" $textTheme={{ style: 'headline2-medium' }} color={theme.colors.text.primary}>
97+
OOTD 탈퇴 전 확인하세요!
98+
<Skeleton width="100%" height={25} />
99+
</StyledText>
100+
</SubTitle>
101+
</CancelContainer>
102+
<BottomButton content="탈퇴하기" onClick={handleDeleteAccount} disabled={!isChecked} />
103+
</OODDFrame>
104+
);
105+
}
106+
82107
return (
83108
<OODDFrame>
84109
<CancelContainer>

src/pages/Account/AccountSetting/index.tsx

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ import leave from '@assets/default/leave.svg';
1212
import Profile_s from '@assets/default/my-page.svg';
1313

1414
import { OODDFrame } from '@components/Frame/Frame';
15-
import Loading from '@components/Loading/index';
1615
import Modal from '@components/Modal';
16+
import Skeleton from '@components/Skeleton';
1717
import { StyledText } from '@components/Text/StyledText';
1818
import TopBar from '@components/TopBar/index';
1919

@@ -44,8 +44,7 @@ const AccountSetting: React.FC = () => {
4444
setIsLoading(false);
4545
}
4646
};
47-
48-
getUserInfo();
47+
setTimeout(getUserInfo, 1000);
4948
}, []);
5049

5150
const handleConfirmLogout = () => {
@@ -68,7 +67,33 @@ const AccountSetting: React.FC = () => {
6867
};
6968

7069
if (isLoading) {
71-
return <Loading />;
70+
return (
71+
<OODDFrame>
72+
<ProfileEditContainer>
73+
<TopBar text="계정 관리" LeftButtonSrc={back} onClickLeftButton={() => navigate(-1)} />
74+
<ProfilePicWrapper>
75+
<ProfilePic>
76+
<Skeleton width={7.5} height={7.5} borderRadius={5} />
77+
</ProfilePic>{' '}
78+
<Row>
79+
<Skeleton width="60%" height={1.25} />
80+
</Row>
81+
<Row>
82+
<Skeleton width="100%" height={1.25} />
83+
</Row>
84+
</ProfilePicWrapper>
85+
86+
<List>
87+
<ListItem>
88+
<Skeleton width="100%" height={2.5} />
89+
</ListItem>
90+
<ListItem>
91+
<Skeleton width="100%" height={2.5} />
92+
</ListItem>
93+
</List>
94+
</ProfileEditContainer>
95+
</OODDFrame>
96+
);
7297
}
7398

7499
return (

src/pages/Account/AccountSetting/styles.tsx

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,6 @@ export const ProfilePicWrapper = styled.div`
1313
display: flex;
1414
flex-direction: column;
1515
align-items: center;
16-
margin-bottom: 1.25rem;
17-
margin-top: 1.5rem;
1816
`;
1917

2018
export const ProfilePic = styled.div`
@@ -24,7 +22,6 @@ export const ProfilePic = styled.div`
2422
border-radius: 50%;
2523
overflow: hidden;
2624
margin-top: 2.125rem;
27-
margin-bottom: 1.375rem;
2825
2926
img {
3027
width: 100%;
@@ -42,7 +39,7 @@ export const Row = styled.div`
4239
justify-content: center;
4340
align-items: center;
4441
width: 100%;
45-
margin-bottom: 0.625rem;
42+
margin-top: 10px;
4643
4744
${Label} {
4845
width: auto;
@@ -67,7 +64,8 @@ export const List = styled.ul`
6764
export const ListItem = styled.li`
6865
display: flex;
6966
align-items: center;
70-
padding: 0.9375rem 1.25rem;
67+
padding: 15px 10px;
68+
7169
border-bottom: 0px solid ${({ theme }) => theme.colors.background.divider};
7270
cursor: pointer;
7371

src/pages/Post/index.tsx

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,18 @@ import { modifyPostRepresentativeStatusApi, deletePostApi } from '@apis/post';
77
import { isPostRepresentativeAtom, postIdAtom, userAtom } from '@recoil/Post/PostAtom';
88
import { getCurrentUserId } from '@utils/getCurrentUserId';
99

10+
import back from '@assets/arrow/left.svg';
1011
import Delete from '@assets/default/delete.svg';
1112
import Edit from '@assets/default/edit.svg';
1213
import Pin from '@assets/default/pin.svg';
1314

1415
import BottomSheet from '@components/BottomSheet';
1516
import BottomSheetMenu from '@components/BottomSheet/BottomSheetMenu';
1617
import OptionsBottomSheet from '@components/BottomSheet/OptionsBottomSheet';
18+
import { OODDFrame } from '@components/Frame/Frame';
1719
import Modal from '@components/Modal';
20+
import Skeleton from '@components/Skeleton';
21+
import TopBar from '@components/TopBar/index';
1822

1923
import type { BottomSheetMenuProps } from '@components/BottomSheet/BottomSheetMenu/dto';
2024
import type { BottomSheetProps } from '@components/BottomSheet/dto';
@@ -23,6 +27,8 @@ import type { ModalProps } from '@components/Modal/dto';
2327

2428
import PostBase from './PostBase/index';
2529

30+
import { PicWrapper, NameWrapper, InfoWrapper, PostWrapper } from './styles';
31+
2632
const Post: React.FC = () => {
2733
const user = useRecoilValue(userAtom);
2834
const postId = useRecoilValue(postIdAtom);
@@ -37,6 +43,8 @@ const Post: React.FC = () => {
3743
const [modalContent, setModalContent] = useState('');
3844
const [postPinStatus, setPostPinStatus] = useState<'지정' | '해제'>('지정');
3945

46+
const [isLoading, setIsLoading] = useState(true);
47+
4048
const navigate = useNavigate();
4149
const currentUserId = getCurrentUserId();
4250

@@ -87,6 +95,10 @@ const Post: React.FC = () => {
8795
};
8896

8997
useEffect(() => {
98+
setTimeout(() => {
99+
setIsLoading(false);
100+
}, 1000);
101+
90102
// 현재 게시글이 내 게시글인지 확인
91103
if (user?.id && postId) {
92104
setIsMyPost(currentUserId === user.id);
@@ -163,6 +175,25 @@ const Post: React.FC = () => {
163175
content: modalContent,
164176
};
165177

178+
if (isLoading) {
179+
return (
180+
<OODDFrame>
181+
<TopBar LeftButtonSrc={back} onClickLeftButton={() => navigate(-1)} />
182+
<InfoWrapper>
183+
<PicWrapper>
184+
<Skeleton width={2.5} height={2.5} borderRadius={2.5} />
185+
</PicWrapper>
186+
<NameWrapper>
187+
<Skeleton width={6.25} height={1.25} />
188+
</NameWrapper>
189+
</InfoWrapper>
190+
<PostWrapper>
191+
<Skeleton width="100%" height={50} />
192+
</PostWrapper>
193+
</OODDFrame>
194+
);
195+
}
196+
166197
return (
167198
<>
168199
<PostBase onClickMenu={handleMenuOpen} />

src/pages/Post/styles.tsx

Lines changed: 16 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,21 @@
11
import { styled } from 'styled-components';
22

3-
export const InputLayout = styled.div`
3+
export const InfoWrapper = styled.div`
44
display: flex;
5-
flex-direction: column;
6-
justify-content: center;
7-
align-items: center;
5+
flex-direction: row;
6+
align-items: left;
7+
`;
8+
9+
export const PicWrapper = styled.div`
10+
margin-left: 47px;
11+
`;
12+
13+
export const NameWrapper = styled.div`
14+
margin-left: 10px;
15+
margin-top: 10px;
16+
`;
817

9-
textarea {
10-
display: block;
11-
width: calc(100% - 3rem);
12-
height: 5.75rem;
13-
border-radius: 0.125rem;
14-
border: 0.0625rem solid ${({ theme }) => theme.colors.gray[600]};
15-
margin-bottom: 5.875rem;
16-
z-index: 2;
17-
margin-top: -3.75rem;
18-
outline: none;
19-
padding: 0.8125rem 0.9375rem;
20-
font-family: 'Pretendard Variable';
21-
font-size: 1rem;
22-
font-style: normal;
23-
font-weight: 300;
24-
line-height: 150%;
25-
color: ${({ theme }) => theme.colors.text.primary};
26-
resize: none;
27-
}
18+
export const PostWrapper = styled.div`
19+
margin-top: 10px;
20+
padding-inline: 30px;
2821
`;

src/pages/Profile/ButtonSecondary/styles.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { styled } from 'styled-components';
22

33
export const Button = styled.button`
44
width: 90%;
5-
margin: 1rem auto;
5+
margin: 16px auto;
66
height: 3.1rem;
77
text-align: center;
88
color: ${({ theme }) => theme.colors.brand.primary};

0 commit comments

Comments
 (0)