Skip to content

Commit 07a86f1

Browse files
committed
[feat] added Slide Component
- added height, maxHeight props at Modal Component
1 parent 93039c3 commit 07a86f1

File tree

8 files changed

+217
-9
lines changed

8 files changed

+217
-9
lines changed

src/app/(MainLayout)/components/ReadOnlyCommonFeed/ReadOnlyCommonFeed.module.scss

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,19 @@
55
width: 100%;
66
height: 100%;
77
max-height: 800px;
8+
overflow: hidden;
89

910
.left {
10-
width: 670px;
11+
width: 60%;
12+
max-width: 670px;
13+
height: 100%;
1114
border-right: 1px solid $COLOR_GRAY_2;
1215
}
1316

1417
.right {
18+
width: 40%;
1519
display: flex;
1620
flex-direction: column;
17-
flex: 1;
1821
}
1922

2023
.right_up {

src/app/(MainLayout)/components/ReadOnlyCommonFeed/ReadOnlyCommonFeed.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import FeedCommentList from '../FeedCommentList';
88
import styles from './ReadOnlyCommonFeed.module.scss';
99
import FeedLikeBox from '../FeedLikeBox';
1010
import FeedCommentWriteInput from '../FeedCommentWriteInput';
11+
import Slide from '../Slide';
1112

1213
const MOCK_FEED_DATA = {
1314
username: 'seongjin',
@@ -30,7 +31,13 @@ const ReadOnlyCommonFeed = () => {
3031

3132
return (
3233
<div className={styles.container}>
33-
<div className={styles.left}>Left</div>
34+
<div className={styles.left}>
35+
<Slide>
36+
<div>111111111111111111111111111111111111111111111111111111</div>
37+
<div>222222222222222222222222222222222222222222222222222222</div>
38+
<div>333333333333333333333333333333333333333333333333333333</div>
39+
</Slide>
40+
</div>
3441
<div className={styles.right}>
3542
<div className={styles.right_up}>
3643
<FeedTextContent feedData={MOCK_FEED_DATA} />
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
@use '../../../../styles/helpers/index' as *;
2+
3+
.container {
4+
position: relative;
5+
display: flex;
6+
align-items: center;
7+
justify-content: center;
8+
width: 100%;
9+
height: 100%;
10+
overflow: hidden;
11+
}
12+
13+
.slide_wrapper {
14+
display: flex;
15+
width: 100%;
16+
height: 100%;
17+
transition: transform 0.5s ease;
18+
}
19+
20+
.slide_item {
21+
width: 100%;
22+
height: 100%;
23+
}
24+
25+
.empty_icon {
26+
width: 35px;
27+
height: 35px;
28+
}
29+
30+
.arrow {
31+
position: absolute;
32+
display: flex;
33+
align-items: center;
34+
justify-content: center;
35+
width: 35px;
36+
height: 35px;
37+
border-radius: 9999px;
38+
background-color: $COLOR_GRAY_6;
39+
top: 50%;
40+
transform: translateY(-50%);
41+
cursor: pointer;
42+
}
43+
44+
.left_arrow {
45+
left: 30px;
46+
}
47+
48+
.right_arrow {
49+
right: 30px;
50+
}
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
'use client';
2+
3+
import {
4+
Children,
5+
ReactNode,
6+
useCallback,
7+
useEffect,
8+
useRef,
9+
useState,
10+
} from 'react';
11+
import clsx from 'clsx';
12+
13+
import LeftArrowIcon from '@/assets/icons/left_arrow.svg';
14+
import RightArrowIcon from '@/assets/icons/right_arrow.svg';
15+
16+
import styles from './Slide.module.scss';
17+
18+
interface SlideProps {
19+
children: ReactNode;
20+
leftIcon?: ReactNode;
21+
rightIcon?: ReactNode;
22+
}
23+
24+
const Slide = ({ children, leftIcon, rightIcon }: SlideProps) => {
25+
const [currentIndex, setCurrentIndex] = useState(0);
26+
const [parentWidth, setParentWidth] = useState<number | undefined>(
27+
0 || undefined,
28+
);
29+
const containerRef = useRef<HTMLUListElement>(null);
30+
31+
const updateParentWidth = useCallback(() => {
32+
if (containerRef.current) {
33+
setParentWidth(containerRef.current.offsetWidth);
34+
}
35+
}, []);
36+
37+
// ? debounce 적용 함수
38+
// const debouncedUpdateParentWidth = useCallback(
39+
// debounce(updateParentWidth, 200),
40+
// [updateParentWidth],
41+
// );
42+
43+
useEffect(() => {
44+
updateParentWidth();
45+
}, [updateParentWidth]);
46+
47+
useEffect(() => {
48+
window.addEventListener('resize', () => updateParentWidth);
49+
50+
return () => {
51+
window.removeEventListener('resize', () => updateParentWidth);
52+
};
53+
}, [updateParentWidth]);
54+
55+
const slideItems =
56+
Children.map<ReactNode, ReactNode>(children, (childElement) => (
57+
<li
58+
className={styles.slide_item}
59+
style={{ width: parentWidth, flexShrink: 0 }}
60+
>
61+
{childElement}
62+
</li>
63+
)) ?? [];
64+
65+
const handleNext = () => {
66+
setCurrentIndex((prevIndex) => (prevIndex + 1) % slideItems.length);
67+
};
68+
69+
const handlePrev = () => {
70+
setCurrentIndex((prevIndex) =>
71+
prevIndex === 0 ? slideItems.length - 1 : prevIndex - 1,
72+
);
73+
};
74+
75+
const hasNext = currentIndex < slideItems.length - 1;
76+
const hasPrev = currentIndex > 0;
77+
78+
return (
79+
<ul className={styles.container} ref={containerRef}>
80+
<div
81+
className={styles.slide_wrapper}
82+
style={{
83+
transform: `translateX(-${currentIndex * (parentWidth ?? 0)}px)`,
84+
}}
85+
>
86+
{slideItems}
87+
</div>
88+
89+
{hasPrev && (
90+
<button
91+
className={clsx(styles.arrow, styles.left_arrow)}
92+
onClick={handlePrev}
93+
>
94+
{leftIcon ?? <LeftArrowIcon />}
95+
</button>
96+
)}
97+
{hasNext && (
98+
<button
99+
className={clsx(styles.arrow, styles.right_arrow)}
100+
onClick={handleNext}
101+
>
102+
{rightIcon ?? <RightArrowIcon />}
103+
</button>
104+
)}
105+
</ul>
106+
);
107+
};
108+
109+
export default Slide;
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { default } from './Slide';

src/components/common/FeedWrapper/FeedWrapper.tsx

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,25 @@
11
'use client';
22

3-
import { ReactNode } from 'react';
3+
import { CSSProperties, ReactNode } from 'react';
44
import { useRouter, useSearchParams } from 'next/navigation';
55

66
import Modal from '../Modal';
77

88
interface FeedWrapperProps {
99
children?: ReactNode;
10-
modalWidth?: string;
10+
modalWidth?: CSSProperties['width'];
11+
modalMaxWidth?: CSSProperties['maxWidth'];
12+
modalHeight?: CSSProperties['height'];
13+
modalMaxHeight?: CSSProperties['maxHeight'];
1114
}
1215

13-
const FeedWrapper = ({ modalWidth = '1070px', children }: FeedWrapperProps) => {
16+
const FeedWrapper = ({
17+
modalWidth = '90vw',
18+
modalMaxWidth = '1070px',
19+
modalHeight = '80vw',
20+
modalMaxHeight = '800px',
21+
children,
22+
}: FeedWrapperProps) => {
1423
const router = useRouter();
1524
const searchParams = new URLSearchParams(useSearchParams()).get('f');
1625

@@ -24,7 +33,13 @@ const FeedWrapper = ({ modalWidth = '1070px', children }: FeedWrapperProps) => {
2433
}
2534

2635
return (
27-
<Modal width={modalWidth} onClose={closeFeed}>
36+
<Modal
37+
width={modalWidth}
38+
maxWidth={modalMaxWidth}
39+
height={modalHeight}
40+
maxHeight={modalMaxHeight}
41+
onClose={closeFeed}
42+
>
2843
{children}
2944
</Modal>
3045
);

src/components/common/Modal/Modal.tsx

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,20 @@ import styles from './modal.module.scss';
44
type ModalProps = {
55
children?: ReactNode;
66
width?: CSSProperties['width'];
7+
maxWidth?: CSSProperties['maxWidth'];
8+
height?: CSSProperties['height'];
9+
maxHeight?: CSSProperties['maxHeight'];
710
onClose?: (status: boolean) => void;
811
};
912

10-
const Modal = ({ children, width = '800px', onClose }: ModalProps) => {
13+
const Modal = ({
14+
children,
15+
width = '800px',
16+
maxWidth,
17+
height,
18+
maxHeight,
19+
onClose,
20+
}: ModalProps) => {
1121
const handleContainerClick = (e: MouseEvent<HTMLDivElement>) => {
1222
e.stopPropagation();
1323
};
@@ -25,7 +35,7 @@ const Modal = ({ children, width = '800px', onClose }: ModalProps) => {
2535
/>
2636
<div
2737
className={styles.container}
28-
style={{ width }}
38+
style={{ width, maxWidth, height, maxHeight }}
2939
onClick={handleContainerClick}
3040
role="presentation"
3141
>

src/utils/event-optimizer.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
2+
const debounce = <T extends (...args: any[]) => any>(
3+
func: T,
4+
delay: number,
5+
) => {
6+
let timer: NodeJS.Timeout;
7+
return (...args: Parameters<T>): void => {
8+
clearTimeout(timer);
9+
timer = setTimeout(() => func(...args), delay);
10+
};
11+
};
12+
13+
export { debounce };

0 commit comments

Comments
 (0)