Skip to content
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

Merged
merged 92 commits into from
Oct 2, 2023
Merged
Show file tree
Hide file tree
Changes from 75 commits
Commits
Show all changes
92 commits
Select commit Hold shift + click to select a range
0b2381c
chore: vite 설치
gabrielyoon7 Sep 13, 2023
8e49320
chore: typescript for vite 설정 수정
gabrielyoon7 Sep 16, 2023
fe96857
chore: vite의 기본 context path 설정
gabrielyoon7 Sep 16, 2023
9afcfda
chore: vite의 github pages 자동 배포 설정
gabrielyoon7 Sep 16, 2023
f0b32f9
chore: vite의 github pages 자동 배포 설정 (오탈자 수정)
gabrielyoon7 Sep 16, 2023
0d55c95
chore: vite의 github pages 자동 배포 설정 (오탈자 수정)
gabrielyoon7 Sep 16, 2023
7b75ae2
chore: vite의 github pages 자동 배포 설정 (오탈자 수정)
gabrielyoon7 Sep 16, 2023
f6f6880
chore: vite의 github pages 자동 배포 설정 (오탈자 수정)
gabrielyoon7 Sep 16, 2023
e9dafd1
chore: vite의 github pages 자동 배포를 yarn이 설치하도록 변경
gabrielyoon7 Sep 16, 2023
b2f4827
chore: vite의 github pages 자동 배포를 yarn이 yarn.lock 파일을 참조하지 못하도록 변경
gabrielyoon7 Sep 16, 2023
109919b
chore: storybook 설치
gabrielyoon7 Sep 17, 2023
cf46080
chore: chromatic 설치
gabrielyoon7 Sep 17, 2023
8ed89ff
docs: 리드미 작성
gabrielyoon7 Sep 17, 2023
8d140a4
chore: chromatic 자동 배포 스크립트 추가
gabrielyoon7 Sep 17, 2023
d079e66
fix: 누락된 스토리북 파일 추가
gabrielyoon7 Sep 17, 2023
c8684b5
chore: styled-components 설치
gabrielyoon7 Sep 17, 2023
fd1aa03
chore: css 초기화
gabrielyoon7 Sep 17, 2023
f9c0d8e
chore: react-router-dom 설치 및 기본 페이지 추가
gabrielyoon7 Sep 17, 2023
30489bb
refactor: html, body, #root 기본 영역 설정
gabrielyoon7 Sep 17, 2023
88d5b41
refactor: vite 기본 index.css 제거
gabrielyoon7 Sep 17, 2023
b6a6056
refactor: storybook 기본 파일 제거
gabrielyoon7 Sep 17, 2023
0634417
feat: Container 컴포넌트 구현
gabrielyoon7 Sep 17, 2023
6eee7fb
feat: Container 예제 페이지 구현
gabrielyoon7 Sep 17, 2023
61a63d8
refactor: storybook 예제 개선
gabrielyoon7 Sep 17, 2023
df11769
Update README.md
gabrielyoon7 Sep 17, 2023
55600f1
docs: 리드미 수정
gabrielyoon7 Sep 17, 2023
2821779
feat: Grid 구현 및 예제 추가
gabrielyoon7 Sep 17, 2023
9fca256
refactor: Grid 스타일 분리
gabrielyoon7 Sep 17, 2023
c20ccc6
refactor: Grid 스토리북 작성
gabrielyoon7 Sep 17, 2023
6e1b28c
feat: Flex 컴포넌트 작성
gabrielyoon7 Sep 17, 2023
fe4448e
refactor: Flex 스타일드 컴포넌트 분리
gabrielyoon7 Sep 17, 2023
6bcbf25
test: Flex 스토리북 작성
gabrielyoon7 Sep 17, 2023
d0aecaf
refactor: ThemeProvider 도입 및 spacing 정의
gabrielyoon7 Sep 18, 2023
1d706ee
refactor: ThemeProvider의 spacing 적용
gabrielyoon7 Sep 18, 2023
f6ff274
test: MinWidth 예제 추가
gabrielyoon7 Sep 18, 2023
e3429ed
fix: Unknown Prop Warning 문제 해결
gabrielyoon7 Sep 18, 2023
9ceeb46
fix: Unknown Prop Warning 문제 해결
gabrielyoon7 Sep 18, 2023
4295de7
fix: Unknown Prop Warning 문제 해결
gabrielyoon7 Sep 18, 2023
f85c013
fix: key가 없는 오류 수정
gabrielyoon7 Sep 18, 2023
bf5f8cb
refactor: 기본 배경에 노란색 적용
gabrielyoon7 Sep 18, 2023
3d51cc4
test: Flex props 설명 추가
gabrielyoon7 Sep 18, 2023
1d7b81a
refactor: 타입 위치 수정
gabrielyoon7 Sep 18, 2023
36a8f45
fix: 웹스톰 오류 수정
gabrielyoon7 Sep 18, 2023
debd0ce
style: prettier 일괄 적용
gabrielyoon7 Sep 18, 2023
b9dbe63
refactor: lib 폴더 적용
gabrielyoon7 Sep 18, 2023
8c8b981
chore: path @types/node vite-plugin-dts 설치
gabrielyoon7 Sep 18, 2023
a3ea011
chore: 배포를 위한 빌드 설정
gabrielyoon7 Sep 18, 2023
bf49e5a
refactor: 구조 개선
gabrielyoon7 Sep 18, 2023
d7cf2ca
chore: npm 배포 설정
gabrielyoon7 Sep 18, 2023
3e54c5d
chore: npm 배포 설정
gabrielyoon7 Sep 18, 2023
f12fabf
refactor: 미사용 코드 제거
gabrielyoon7 Sep 18, 2023
8e5abc3
chore: 배포 오류 수정
gabrielyoon7 Sep 18, 2023
2a60d90
docs: readme 수정
gabrielyoon7 Sep 18, 2023
9eef417
chore: 배포 설정 수정
gabrielyoon7 Sep 18, 2023
8cb7c86
chore: 배포 설정 수정
gabrielyoon7 Sep 18, 2023
7d7915a
chore: github 자동 배포 설정 수정
gabrielyoon7 Sep 18, 2023
5cd49ee
chore: github pages 자동 배포 오류 수정
gabrielyoon7 Sep 18, 2023
91c12a4
chore: github pages 자동 배포 오류 수정
gabrielyoon7 Sep 18, 2023
5e77b85
refactor: 그리드의 속성을 옵셔널 하게 변경
gabrielyoon7 Sep 18, 2023
902470d
docs: readme 작성
gabrielyoon7 Sep 18, 2023
cc09fae
refactor: 불필요한 코드 및 파일 제거
gabrielyoon7 Sep 24, 2023
c68852d
feat: SplitPane 뼈대 파일 추가
gabrielyoon7 Sep 25, 2023
4a8d256
refactor: styled-components 분리
gabrielyoon7 Sep 25, 2023
f24ada6
refactor: 주석 추가
gabrielyoon7 Sep 25, 2023
0d9f49a
docs: 리드미 수정
gabrielyoon7 Sep 25, 2023
f472124
refactor: 배포 설정 수정
gabrielyoon7 Sep 25, 2023
4e37a25
docs: 스토리북 배포 링크 수정
gabrielyoon7 Sep 25, 2023
ab9debe
refactor: SplitPane 배포 설정
gabrielyoon7 Sep 25, 2023
338b838
refactor: SplitPane 배포 설정
gabrielyoon7 Sep 25, 2023
ff44c2e
test: 스토리북 예제 추가
gabrielyoon7 Sep 25, 2023
fd69069
docs: readme 주소 개선
gabrielyoon7 Sep 25, 2023
b621af7
chore: npmjs 버전 수정
gabrielyoon7 Sep 25, 2023
fb8a9f4
Merge branch 'gabrielyoon7' into step2
gabrielyoon7 Sep 25, 2023
790ccdb
test: 중첩 예제 추가
gabrielyoon7 Sep 26, 2023
e2cd604
Merge remote-tracking branch 'origin/step2' into step2
gabrielyoon7 Sep 26, 2023
fa867ad
refactor: SplitPaneContainer의 children 속성을 필수로 지정
gabrielyoon7 Sep 29, 2023
72b6979
refactor: SplitPane의 타입 오류 메시지가 출력되지 않도록 개선
gabrielyoon7 Sep 29, 2023
37143e8
refactor: 가운데 조절 막대의 디자인을 개선
gabrielyoon7 Sep 29, 2023
7b05fb0
refactor: 모바일 터치 대응
gabrielyoon7 Sep 29, 2023
0b97ba0
chore: vitest 설치
gabrielyoon7 Sep 29, 2023
3231d5c
refactor: ErrorBoundary.tsx 구현 및 적용
gabrielyoon7 Sep 30, 2023
ffea388
refactor: 자식의 길이가 2가 아닌 경우 경고
gabrielyoon7 Sep 30, 2023
154b692
refactor: 불필요한 속성 제거
gabrielyoon7 Sep 30, 2023
63089e3
refactor: 속성 값에 대한 검사를 진행
gabrielyoon7 Sep 30, 2023
7bf23c6
refactor: 다시 시도하기 버튼 구현
gabrielyoon7 Sep 30, 2023
60b2f74
fix: 영역 설정이 제대로 되지 않는 문제를 수정
gabrielyoon7 Sep 30, 2023
e68197f
refactor: 프롭 검사기 추가
gabrielyoon7 Sep 30, 2023
6f7daf0
refactor: constants 분리
gabrielyoon7 Sep 30, 2023
0903fc8
test: 테스트 케이스 추가
gabrielyoon7 Sep 30, 2023
7dc2aad
chore: readme 및 버전 수정
gabrielyoon7 Sep 30, 2023
ab50987
chore: readme 및 버전 수정
gabrielyoon7 Sep 30, 2023
d8f6f68
refactor: 불필요한 출력문 제거
gabrielyoon7 Sep 30, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: Deploy static content to Pages

on:
push:
branches: ["main", "gabrielyoon7", "step1", "step2"]
branches: [ "step1", "step2" ]

workflow_dispatch:

Expand Down
28 changes: 25 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ function MyComponent() {

```

[Container 스토리북 보러가기](https://65068c38c8cef469d3e5e634-otupvlrdhy.chromatic.com/?path=/docs/components-container--docs)
[Container 스토리북 보러가기](https://65068c38c8cef469d3e5e634-vmefemwmwj.chromatic.com/?path=/docs/components-container--docs)

## Grid

Expand Down Expand Up @@ -60,7 +60,7 @@ function MyComponent() {

```

[Grid 스토리북 보러가기](https://65068c38c8cef469d3e5e634-otupvlrdhy.chromatic.com/?path=/docs/components-grid--docs)
[Grid 스토리북 보러가기](https://65068c38c8cef469d3e5e634-vmefemwmwj.chromatic.com/?path=/docs/components-grid--docs)

## Flex

Expand All @@ -82,5 +82,27 @@ function MyComponent() {
}

```
[Flex 스토리북 보러가기](https://65068c38c8cef469d3e5e634-vmefemwmwj.chromatic.com/?path=/docs/components-flex--docs)

## SplitPane

### 소개

이 컴포넌트는 SplitPane 레이아웃을 구현하기 위한 React 컴포넌트입니다. 이 컴포넌트를 사용하면 UI 디자인에서 SplitPane 레이아웃을 쉽게 구현할 수 있습니다.

```typescript jsx
import {SplitPane} from "@gabrielyoon7/layout-component";

function MyComponent() {
return (
<SplitPane defaultSize="50%" minSize="10%" maxSize="90%">
<div style={{backgroundColor: 'yellow', height: '100px'}}/>
<div style={{backgroundColor: 'yellowgreen', height: '100px'}}/>
</SplitPane>
)
}

```

[SplitPane 스토리북 보러가기](https://65068c38c8cef469d3e5e634-vmefemwmwj.chromatic.com/?path=/docs/components-splitpane--docs)

[Flex 스토리북 보러가기](https://65068c38c8cef469d3e5e634-otupvlrdhy.chromatic.com/?path=/docs/components-flex--docs)
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@gabrielyoon7/layout-component",
"version": "0.0.5",
"version": "0.0.10",
"description": "A layout component for React (with woowacourse)",
"type": "module",
"main": "dist/index.umd.cjs",
Expand Down
1 change: 1 addition & 0 deletions src/lib/index.tsx
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";
86 changes: 86 additions & 0 deletions src/lib/layouts/SplitPane/SplitPane.stories.tsx
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>
);
23 changes: 23 additions & 0 deletions src/lib/layouts/SplitPane/SplitPane.styles.ts
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;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

세로로 조절하는건 혹시 고려하셨는데 하지 않으신건가요?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

세로 기능은 고려하지 않았습니다!

height: 100%;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

해당 속성이 없어도 변경사항이 발생하지 않는 것 같은데 어떤 이유로 넣어주신 건가요?

Copy link
Member Author

Choose a reason for hiding this comment

The 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%'};
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

사용자가 maxSize를 입력하지 않으면 100%가 되는 것 같은데, 이렇게 되면 조절바가 움직이지 않습니다!
예상으로는 조절바가 3px를 차지하고 있어서 그런가 싶기도 한데, 정확하지는 않습니다😂

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

수정하였습니다

`;

export const Resizer = styled.div`
width: 3px;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

현재 바에는 색이 들어있지 않는데 색을 받아서 넣는 건 어떻게 생각하세요?
취향에 따라 구분선을 원하는 경우도 있을 것 같아요.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

색상 대신 구분선을 추가했습니다

cursor: col-resize;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오 새로운 속성 배워갑니당

`;
39 changes: 39 additions & 0 deletions src/lib/layouts/SplitPane/SplitPane.tsx
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[];
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

children은 왜 otpional props인가요?

Copy link
Member Author

Choose a reason for hiding this comment

The 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}>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

{flex: 1}을 처음 봤는데 실무에서 많이 사용된다고 하네요.
저도 이런 작동이 되도록 고민 많이했는데 해당 코드를 참고하겠습니다👍

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 코드는 사라졌습니다 🥲🥲

{children[1]}
</ResizablePane>
</>
) : <div>반드시 2개의 컴포넌트가 존재해야 합니다.</div>}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

예외처리 좋아요

</SplitPaneContainer>
);
}

export default SplitPane;
3 changes: 3 additions & 0 deletions src/lib/layouts/SplitPane/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import SplitPane from "./SplitPane.tsx";

export default SplitPane;
67 changes: 67 additions & 0 deletions src/lib/layouts/SplitPane/useSplitPane.ts
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,
};
}