Skip to content

Commit

Permalink
feat : 편지 작성 페이지 개발 (#128)
Browse files Browse the repository at this point in the history
* env : react-spring-bottom-sheet - > alpha 버전으로 마이그레이션

* env : 필요 의존성 설치

* chore : 하위 컴포넌트 애니메이션 수정

* feat : form 개발

* feat : 폰트, 이미지 선택 기능

* refactor : 페이지 분리
  • Loading branch information
HelloWook authored Nov 29, 2024
1 parent c646830 commit b3e4aab
Show file tree
Hide file tree
Showing 12 changed files with 417 additions and 106 deletions.
297 changes: 208 additions & 89 deletions .pnp.cjs

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"react-router-dom": "^7.0.1",
"react-spring": "^9.7.4",
"react-spring-bottom-sheet": "3.5.0-alpha.0",
"react-textarea-autosize": "^8.5.5",
"sharp": "^0.33.5",
"swiper": "^11.1.15",
"ts-pattern": "^5.5.0",
Expand Down
1 change: 1 addition & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import ToastContainer from './components/Common/ToastContainer/ToastContainer';
import { RouterProvider } from 'react-router-dom';
import { router } from '@/router';

export const App = () => {
return (
<>
Expand Down
33 changes: 26 additions & 7 deletions src/components/Common/ItemSlider/ItemSlider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,22 @@ type ItemSliderProps = {
width?: string;
height?: string;
spaceBetween?: number;
value: string;
setValue: (value: string) => void; // value를 변경할 수 있는 setValue 함수
};

export const ItemSlider = ({
itemType,
itemIDList,
width,
height,
spaceBetween = 10
spaceBetween = 10,
value,
setValue
}: ItemSliderProps) => {
const slideStyle: CSSProperties = {
width: width ? width : 'auto',
height: height ? height : 'auto'
width: 'auto',
height: 'auto'
};

const getImagePath = (id: string) => {
Expand All @@ -44,17 +48,30 @@ export const ItemSlider = ({
switch (itemType) {
case 'text':
return (
<div className="flex items-center justify-center h-full p-2">
<div
className="flex items-center justify-center h-full p-2 cursor-pointer"
onClick={() => {
setValue(item.name);
console.log(item.name);
}} // 아이템 클릭 시 setValue 호출
>
{item.name}
</div>
);
case 'image':
return (
<div className="flex items-center justify-center h-full">
<div
className="flex items-center justify-center h-full cursor-pointer"
onClick={() => {
setValue(item.id);
console.log(item.id);
}} // 아이템 클릭 시 setValue 호출
>
<img
className="object-cover w-full h-full rounded"
className="object-cover rounded"
src={getImagePath(item.id)}
alt={item.name}
style={{ width: width, height: height }}
/>
</div>
);
Expand All @@ -76,7 +93,9 @@ export const ItemSlider = ({
<SwiperSlide
style={slideStyle}
key={item.id}
className="flex justify-center align-middle rounded-md bg-slate-200"
className={`flex justify-center align-middle rounded-md bg-slate-200 ${
item.id === value ? 'bg-blue-200' : '' // 선택된 아이템 배경 색 변경
}`}
>
{getSliderContent(item)}
</SwiperSlide>
Expand Down
3 changes: 2 additions & 1 deletion src/components/Common/TextArea/TextArea.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { Meta, StoryObj } from '@storybook/react';
import React, { useState } from 'react';

import { TextArea } from './TextArea';

Expand All @@ -13,5 +14,5 @@ export default meta;
type Story = StoryObj<typeof TextArea>;

export const Default: Story = {
args: {}
args: { value: '즐겁다' }
};
4 changes: 3 additions & 1 deletion src/components/Common/TextArea/TextArea.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ import React, { useRef, useEffect } from 'react';
type TextAreaProps = {
value: string;
setValue: (value: string) => void;
font?: string;
};

export const TextArea = ({ value, setValue }: TextAreaProps) => {
export const TextArea = ({ value, setValue, font }: TextAreaProps) => {
const textAreaRef = useRef<HTMLTextAreaElement>(null);

const handleInputChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
Expand Down Expand Up @@ -39,6 +40,7 @@ export const TextArea = ({ value, setValue }: TextAreaProps) => {
return (
<textarea
className="absolute w-9/12 px-2 bg-transparent border-none resize-none mt-[42%] leading-[260%] overflow-hidden h-auto"
style={{ fontFamily: font || 'inherit' }}
ref={textAreaRef}
placeholder="편지를 작성하세요..."
value={value}
Expand Down
10 changes: 6 additions & 4 deletions src/components/Common/Toggle/Toggle.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,28 @@ export type ToggleProps = {

export const Toggle = ({ isChecked, onToggle }: ToggleProps) => {
return (
<div className="relative flex items-center justify-center w-[160px] h-[30px] bg-gray-200 rounded-full p-1">
<div className="relative flex items-center justify-between w-[160px] h-[30px] bg-gray-200 rounded-full p-1">
<div
onClick={onToggle}
className={`absolute w-[78px] h-[30px] bg-gray-100 rounded-full cursor-pointer transition-all duration-200 ease-in-out ${
isChecked ? 'right-0' : 'left-0'
isChecked ? 'translate-x-[78px]' : 'translate-x-0'
}`}
/>
<div
onClick={onToggle}
className={`absolute top-1/2 left-7 cursor-pointer transform -translate-y-1/2 font-bold text-sm ${
className={`absolute left-2 font-bold text-sm transform -translate-y-1/2 ${
isChecked ? 'text-gray-400' : 'text-black'
}`}
style={{ top: '50%' }}
>
편지지
</div>
<div
onClick={onToggle}
className={`absolute cursor-pointer top-1/2 left-[85px] transform -translate-y-1/2 font-bold text-sm ${
className={`absolute right-2 font-bold text-sm transform -translate-y-1/2 ${
isChecked ? 'text-black' : 'text-gray-400'
}`}
style={{ top: '50%' }}
>
글씨체
</div>
Expand Down
2 changes: 1 addition & 1 deletion src/components/Common/TopBar/TopBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ type TopBaerProps = { onClick: () => void };

export const TopBar = ({ onClick }: TopBaerProps) => {
return (
<div className="flex items-center justify-between w-full px-4 text-2xl ">
<div className="fixed z-10 flex items-center justify-between w-full px-4 text-2xl">
<BackButton onClick={onClick} />
<p className="">완료</p>
</div>
Expand Down
36 changes: 36 additions & 0 deletions src/components/Letter/Post/PostLetterForm.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import type { Meta, StoryObj } from '@storybook/react';

import { PostLetterForm } from './PostLetterForm';

const meta: Meta<typeof PostLetterForm> = {
component: PostLetterForm,
title: 'PostLetterForm',
tags: ['autodocs'],
argTypes: {},
decorators: [
(Story) => (
<div
style={{
width: '375px',
margin: '0 auto',
border: '1px solid #ddd',
padding: '16px',
minHeight: '500px', // 최소 높이 추가
display: 'flex',
flexDirection: 'column', // Flexbox로 하위 요소 정렬
justifyContent: 'center' // 하위 콘텐츠 중앙 정렬
}}
>
<Story />
</div>
)
]
};

export default meta;

type Story = StoryObj<typeof PostLetterForm>;

export const Default: Story = {
args: {}
};
84 changes: 84 additions & 0 deletions src/components/Letter/Post/PostLetterForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { ItemSlider } from '@/components/Common/ItemSlider/ItemSlider';
import { Margin } from '@/components/Common/Margin/Margin';
import { SliderMenuContainer } from '@/components/Common/SliderMenuContainer/SliderMenuContainer';
import { TextArea } from '@/components/Common/TextArea/TextArea';
import { Toggle } from '@/components/Common/Toggle/Toggle';
import { useState } from 'react';

export const PostLetterForm = () => {
const [title, setTtile] = useState('');
const [content, setContent] = useState('');
const [isFont, setIsFont] = useState(true);
const [letter, setLetter] = useState('편지지_샘플_1');
const [font, setFont] = useState('');

const textItems = [
{ name: 'cursive', id: '1' },
{ name: 'fantasy', id: '2' },
{ name: 'initial', id: '3' },
{ name: 'monospace', id: '4' }
];

const imageItems = [
{ id: '편지지_샘플_1', name: '이미지' },
{ id: '편지지_샘플_2', name: '이미지' },
{ id: '편지지_샘플_3', name: '이미지' },
{ id: '편지지_샘플_4', name: '이미지' },
{ id: '편지지_샘플_5', name: '이미지' }
];

return (
<SliderMenuContainer>
<Margin top={18} />
<div>
<div className="relative flex justify-center">
<input
onChange={(e) => setTtile(e.target.value)}
value={title}
type="text"
placeholder="제목을 입력해주세요"
className="absolute mt-[35%] w-9/12 bg-transparent px-2 focus:border-none focus:outline-none border-none "
/>
<TextArea
value={content}
setValue={setContent}
font={font}
/>
</div>

<img
src={`/public/${letter}.png`}
className="w-full h-full rounded-lg "
alt="샘플 편지지"
/>

<div className="flex flex-col items-center justify-center aling bottom-9 ">
<Margin bottom={14} />
{isFont ? (
<ItemSlider
itemType="text"
itemIDList={textItems}
value={font}
setValue={setFont}
/>
) : (
<ItemSlider
itemType="image"
itemIDList={imageItems}
width="77px"
height="99px"
value={letter}
setValue={setLetter}
/>
)}
<Margin bottom={14} />
<Toggle
isChecked={isFont}
onToggle={() => setIsFont(!isFont)}
/>
<Margin bottom={14} />
</div>
</div>
</SliderMenuContainer>
);
};
11 changes: 10 additions & 1 deletion src/pages/Letter/Create/CreateLetterPage.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
import { TopBar } from '@/components/Common/TopBar/TopBar';
import { PostLetterForm } from '@/components/Letter/Post/PostLetterForm';
import React from 'react';

export const CreateLetterPage = () => {
return (
<div className="bg-slate-500 w-[375px] max-w-[768px] h-[815px]"></div>
<div className=" w-[375px] max-w-[768px] h-[815px]">
<TopBar
onClick={() => {
alert('back~');
}}
/>
<PostLetterForm />
</div>
);
};
41 changes: 39 additions & 2 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1142,7 +1142,7 @@ __metadata:
languageName: node
linkType: hard

"@babel/runtime@npm:^7.11.2, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.17.8, @babel/runtime@npm:^7.8.4":
"@babel/runtime@npm:^7.11.2, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.17.8, @babel/runtime@npm:^7.20.13, @babel/runtime@npm:^7.8.4":
version: 7.26.0
resolution: "@babel/runtime@npm:7.26.0"
dependencies:
Expand Down Expand Up @@ -8586,6 +8586,19 @@ __metadata:
languageName: node
linkType: hard

"react-textarea-autosize@npm:^8.5.5":
version: 8.5.5
resolution: "react-textarea-autosize@npm:8.5.5"
dependencies:
"@babel/runtime": "npm:^7.20.13"
use-composed-ref: "npm:^1.3.0"
use-latest: "npm:^1.2.1"
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0
checksum: 10c0/d708a31b39a409d0246cd8afbd956ce51db58ce0b6411b9d4e1dc876ce93c329d20875933ce5d337662fdcd3699596966dc630149236fee2835d74e302404c98
languageName: node
linkType: hard

"react-use-gesture@npm:^8.0.1":
version: 8.0.1
resolution: "react-use-gesture@npm:8.0.1"
Expand Down Expand Up @@ -10299,7 +10312,16 @@ __metadata:
languageName: node
linkType: hard

"use-isomorphic-layout-effect@npm:^1.0.0":
"use-composed-ref@npm:^1.3.0":
version: 1.3.0
resolution: "use-composed-ref@npm:1.3.0"
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0
checksum: 10c0/e64ce52f4b18c020407636784192726807404a2552609acf7497b66a2b7070674fb5d2b950d426c4aa85f353e2bbecb02ebf9c5b865cd06797938c70bcbf5d26
languageName: node
linkType: hard

"use-isomorphic-layout-effect@npm:^1.0.0, use-isomorphic-layout-effect@npm:^1.1.1":
version: 1.1.2
resolution: "use-isomorphic-layout-effect@npm:1.1.2"
peerDependencies:
Expand All @@ -10311,6 +10333,20 @@ __metadata:
languageName: node
linkType: hard

"use-latest@npm:^1.2.1":
version: 1.2.1
resolution: "use-latest@npm:1.2.1"
dependencies:
use-isomorphic-layout-effect: "npm:^1.1.1"
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0
peerDependenciesMeta:
"@types/react":
optional: true
checksum: 10c0/1958886fc35262d973f5cd4ce16acd6ce3a66707a72761c93abd1b5ae64e1a11efa83f68e6c8c9bf1647628037980ce59df64cba50adb36bd4071851e70527d2
languageName: node
linkType: hard

"use-subscription@npm:^1.3.0":
version: 1.8.2
resolution: "use-subscription@npm:1.8.2"
Expand Down Expand Up @@ -10499,6 +10535,7 @@ __metadata:
react-router-dom: "npm:^7.0.1"
react-spring: "npm:^9.7.4"
react-spring-bottom-sheet: "npm:3.5.0-alpha.0"
react-textarea-autosize: "npm:^8.5.5"
sharp: "npm:^0.33.5"
storybook: "npm:8.4.4"
swiper: "npm:^11.1.15"
Expand Down

0 comments on commit b3e4aab

Please sign in to comment.