-
Notifications
You must be signed in to change notification settings - Fork 48
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[고급 레이아웃 컴포넌트] 가브리엘(윤주현) 미션 제출합니다. #62
Changes from 75 commits
0b2381c
8e49320
fe96857
9afcfda
f0b32f9
0d55c95
7b75ae2
f6f6880
e9dafd1
b2f4827
109919b
cf46080
8ed89ff
8d140a4
d079e66
c8684b5
fd1aa03
f9c0d8e
30489bb
88d5b41
b6a6056
0634417
6eee7fb
61a63d8
df11769
55600f1
2821779
9fca256
c20ccc6
6e1b28c
fe4448e
6bcbf25
d0aecaf
1d706ee
f6ff274
e3429ed
9ceeb46
4295de7
f85c013
bf5f8cb
3d51cc4
1d7b81a
36a8f45
debd0ce
b9dbe63
8c8b981
a3ea011
bf49e5a
d7cf2ca
3e54c5d
f12fabf
8e5abc3
2a60d90
9eef417
8cb7c86
7d7915a
5cd49ee
91c12a4
5e77b85
902470d
cc09fae
c68852d
4a8d256
f24ada6
0d9f49a
f472124
4e37a25
ab9debe
338b838
ff44c2e
fd69069
b621af7
fb8a9f4
790ccdb
e2cd604
fa867ad
72b6979
37143e8
7b05fb0
0b97ba0
3231d5c
ffea388
154b692
63089e3
7bf23c6
60b2f74
e68197f
6f7daf0
0903fc8
7dc2aad
ab50987
d8f6f68
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
export {default as Container} from "./layouts/Container/Container"; | ||
export {default as Flex} from "./layouts/Flex/Flex"; | ||
export {default as Grid} from "./layouts/Grid/Grid"; | ||
export {default as SplitPane} from "./layouts/SplitPane/SplitPane"; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
import type {Meta} from "@storybook/react"; | ||
import SplitPane from "./index.ts"; | ||
import {SplitPaneProps} from "./SplitPane.tsx"; | ||
|
||
const meta = { | ||
title: "Components/SplitPane", | ||
component: SplitPane, | ||
tags: ["autodocs"], | ||
args: { | ||
defaultSize: "20%", | ||
minSize: "10%", | ||
maxSize: "90%", | ||
}, | ||
argTypes: { | ||
defaultSize: { | ||
control: { | ||
type: "text", | ||
}, | ||
description: "SplitPane의 기본 사이즈를 설정합니다.", | ||
}, | ||
minSize: { | ||
control: { | ||
type: "text", | ||
}, | ||
description: "SplitPane 좌측 영역의 최소 사이즈를 설정합니다." | ||
}, | ||
maxSize: { | ||
control: { | ||
type: "text", | ||
}, | ||
description: "SplitPane 좌측 영역의 최대 사이즈를 설정합니다." | ||
} | ||
}, | ||
} satisfies Meta<typeof SplitPane>; | ||
|
||
export default meta; | ||
|
||
const loremIpsum = `Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis vulputate nunc eu tempor tempor. Maecenas ipsum nisi, placerat sit amet arcu eu, egestas luctus orci. Etiam sed ante eu metus aliquet dapibus. Nullam eget dolor diam. Praesent et lobortis massa. Proin ornare, risus quis pretium vulputate, lacus lacus vehicula ex, eleifend vestibulum sapien augue et quam. Nulla id augue felis. Quisque vitae elit felis. Vestibulum vitae rhoncus elit. Duis imperdiet tortor vitae tellus vulputate, et sagittis est ullamcorper. Fusce dignissim ultricies tristique. Nullam congue mi ut venenatis pellentesque. Proin egestas sodales volutpat.`; | ||
|
||
export const Default = (args: SplitPaneProps) => ( | ||
<SplitPane {...args}> | ||
<div style={{backgroundColor: 'yellow', height: '100px'}}/> | ||
<div style={{backgroundColor: 'yellowgreen', height: '100px'}}/> | ||
</SplitPane> | ||
); | ||
|
||
export const Texts = (args: SplitPaneProps) => ( | ||
<SplitPane {...args}> | ||
<div> | ||
{loremIpsum} | ||
</div> | ||
<div> | ||
{loremIpsum} | ||
</div> | ||
</SplitPane> | ||
); | ||
|
||
export const WrongChildren = (args: SplitPaneProps) => ( | ||
<SplitPane {...args}> | ||
<div> | ||
{loremIpsum} | ||
</div> | ||
<div> | ||
{loremIpsum} | ||
</div> | ||
<div> | ||
{loremIpsum} | ||
</div> | ||
</SplitPane> | ||
); | ||
|
||
export const Duplicated = (args: SplitPaneProps) => ( | ||
<SplitPane {...args}> | ||
<div> | ||
{loremIpsum} | ||
</div> | ||
<SplitPane {...args}> | ||
<div> | ||
{loremIpsum} | ||
</div> | ||
<div> | ||
{loremIpsum} | ||
</div> | ||
</SplitPane> | ||
</SplitPane> | ||
); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import styled from "styled-components"; | ||
|
||
interface ResizablePaneProps { | ||
minSize?: string; | ||
maxSize?: string; | ||
} | ||
|
||
export const SplitPaneContainer = styled.div` | ||
display: flex; | ||
flex-direction: row; | ||
height: 100%; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 해당 속성이 없어도 변경사항이 발생하지 않는 것 같은데 어떤 이유로 넣어주신 건가요? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 제거하였습니당 |
||
`; | ||
|
||
export const ResizablePane = styled.div<ResizablePaneProps>` | ||
overflow: auto; | ||
min-width: ${({minSize}) => minSize || '0'}; | ||
max-width: ${({maxSize}) => maxSize || '100%'}; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 사용자가 maxSize를 입력하지 않으면 100%가 되는 것 같은데, 이렇게 되면 조절바가 움직이지 않습니다! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 수정하였습니다 |
||
`; | ||
|
||
export const Resizer = styled.div` | ||
width: 3px; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 현재 바에는 색이 들어있지 않는데 색을 받아서 넣는 건 어떻게 생각하세요? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 색상 대신 구분선을 추가했습니다 |
||
cursor: col-resize; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 오 새로운 속성 배워갑니당 |
||
`; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
import {ReactNode} from 'react'; | ||
import {ResizablePane, Resizer, SplitPaneContainer} from "./SplitPane.styles.ts"; | ||
import {useSplitPane} from "./useSplitPane.ts"; | ||
|
||
export interface SplitPaneProps { | ||
defaultSize: string; | ||
minSize?: string; | ||
maxSize?: string; | ||
children?: ReactNode[]; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. children은 왜 otpional props인가요? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 수정하였습니다 |
||
} | ||
|
||
const SplitPane = ({defaultSize, minSize, maxSize, children}: SplitPaneProps) => { | ||
const { | ||
containerRef, | ||
leftPaneRef, | ||
rightPaneRef, | ||
paneSize, | ||
handleMouseDown, | ||
} = useSplitPane(defaultSize, minSize, maxSize); | ||
|
||
|
||
return ( | ||
<SplitPaneContainer ref={containerRef}> | ||
{children && children?.length === 2 ? ( | ||
<> | ||
<ResizablePane style={{flexBasis: paneSize}} minSize={minSize} maxSize={maxSize} ref={leftPaneRef}> | ||
{children[0]} | ||
</ResizablePane> | ||
<Resizer onMouseDown={handleMouseDown}/> | ||
<ResizablePane style={{flex: 1}} minSize={minSize} maxSize={maxSize} ref={rightPaneRef}> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이 코드는 사라졌습니다 🥲🥲 |
||
{children[1]} | ||
</ResizablePane> | ||
</> | ||
) : <div>반드시 2개의 컴포넌트가 존재해야 합니다.</div>} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 예외처리 좋아요 |
||
</SplitPaneContainer> | ||
); | ||
} | ||
|
||
export default SplitPane; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
import SplitPane from "./SplitPane.tsx"; | ||
|
||
export default SplitPane; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
import {useEffect, useRef, useState} from 'react'; | ||
|
||
export const useSplitPane = (defaultSize: string, minSize?: string, maxSize?: string) => { | ||
const [paneSize, setPaneSize] = useState(defaultSize); | ||
const [isResizing, setIsResizing] = useState(false); | ||
|
||
const containerRef = useRef<HTMLDivElement | null>(null); | ||
const leftPaneRef = useRef<HTMLDivElement | null>(null); | ||
const rightPaneRef = useRef<HTMLDivElement | null>(null); | ||
|
||
// 패널 크기 조절 시작 | ||
const handleMouseDown = () => { | ||
setIsResizing(true); | ||
}; | ||
|
||
// 패널 크기 조절 종료 | ||
const handleMouseUp = () => { | ||
setIsResizing(false); | ||
}; | ||
|
||
const handleMouseMove = (e: MouseEvent) => { | ||
// 패널 사이즈가 변경중이라면 | ||
if (!isResizing || !containerRef.current) { | ||
return; | ||
} | ||
|
||
// 패널의 양쪽 끝의 x 좌표를 구함 | ||
const leftPaneLeftX = leftPaneRef.current?.getBoundingClientRect().left || 0; | ||
const rightPanelRightX = rightPaneRef.current?.getBoundingClientRect().right || 0; | ||
|
||
const mouseX = e.clientX; | ||
|
||
// 마우스 위치가 패널 범위를 벗어나면 종료 | ||
if (mouseX < leftPaneLeftX || mouseX > rightPanelRightX) { | ||
return; | ||
} | ||
|
||
const newPaneSize = `${((mouseX - leftPaneLeftX) / (rightPanelRightX - leftPaneLeftX)) * 100}%`; | ||
|
||
// 최소 크기와 최대 크기를 체크하고 업데이트 | ||
if ((minSize && newPaneSize < minSize) || (maxSize && newPaneSize > maxSize)) { | ||
return; | ||
} | ||
|
||
setPaneSize(newPaneSize); | ||
}; | ||
|
||
useEffect(() => { | ||
window.addEventListener('mousemove', handleMouseMove); | ||
window.addEventListener('mouseup', handleMouseUp); | ||
|
||
// 메모리 누수 방지 | ||
return () => { | ||
window.removeEventListener('mousemove', handleMouseMove); | ||
window.removeEventListener('mouseup', handleMouseUp); | ||
}; | ||
}, [isResizing]); | ||
|
||
return { | ||
containerRef, | ||
leftPaneRef, | ||
rightPaneRef, | ||
paneSize, | ||
isResizing, | ||
handleMouseDown, | ||
}; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
세로로 조절하는건 혹시 고려하셨는데 하지 않으신건가요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
세로 기능은 고려하지 않았습니다!