Skip to content

Commit

Permalink
feat: 리뷰 작성 폼 개편 (#77)
Browse files Browse the repository at this point in the history
* feat: 사진 아이콘 추가

* feat: 이미지 업로더 개편

* feat: 별점 작성 개편

* feat: 리뷰 설명 개편

* refactor: 안 쓰는 스타일 파일 삭제

* feat: 재구매 체크박스 개편

* feat: 재구매 타이틀 추가

* feat: 태그 선택 바텀시트 열기 버튼 추가

* feat: 태그 선택 구현

* feat: 태그 최대 3개 선택 구현

* feat: 체크 되어 있는 태그 스타일

* feat: 선택 태그 폼에서 렌더링

* feat: 폼에서 태그 삭제

* refactor: 안 쓰는 컴포넌트 삭제

* feat: 태그 선택 바텀시트 컴포넌트 추가

* feat: 리뷰 등록 페이지 추가

* feat: 태그 바텀시트 prop 추가

* refactor: 리뷰 태그 바텀시트 index export

* refactor: Text 컴포넌트 사용

* style: 태그 조건 이름 수정

* feat: 태그 선택 최소 조건 추가

* feat: 태그 선택 바텀시트 높이 추가

* refactor: Text 컴포넌트 사용

* feat: 이미지 업로더 스타일 개편

* feat: 리뷰 설명 에러 처리

* feat: 설명 에러 메시지 추가 구현

* feat: 리뷰 태그 바텀시트 스토리 추가
  • Loading branch information
Leejin-Yang authored Apr 14, 2024
1 parent 4196d6b commit 0c9edba
Show file tree
Hide file tree
Showing 29 changed files with 728 additions and 439 deletions.
20 changes: 20 additions & 0 deletions .storybook/preview-body.html
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,11 @@
d="M20.2 4.683a1.013 1.013 0 0 1 0 1.438L9.008 17.317a1.013 1.013 0 0 1-1.437 0L1.8 11.55a1.017 1.017 0 0 1 1.437-1.437l5.045 5.045L18.758 4.683a1.013 1.013 0 0 1 1.437 0h.005z"
/>
</symbol>
<symbol id="picture" viewBox="0 0 28 29">
<path
d="M1.977 5.125v19.25h23.625V5.125H1.977zm1.687 1.75h20.25v12.168l-4.456-4.648-.606-.63-3.824 3.966-4.851-5.086-.607-.63-5.906 6.126V6.875zm16.875 1.75c-.933 0-1.687.783-1.687 1.75s.754 1.75 1.687 1.75 1.688-.783 1.688-1.75-.755-1.75-1.688-1.75zM9.57 14.505l7.752 8.12H3.664V20.63l5.906-6.125zm9.282 1.75l5.062 5.25v1.12h-4.192l-3.507-3.664 2.637-2.707z"
/>
</symbol>
<symbol id="recipe" viewBox="0 0 22 26">
<path
d="M20.675 12.44c-.324-.604-.643-1.123-.868-1.533a5.058 5.058 0 0 1-.256-.516.808.808 0 0 1-.068-.244v-.005.001a.311.311 0 0 1 .054-.168.344.344 0 0 1 .116-.096.83.83 0 0 1 .135-.06l.096.045.25-.053c.53-.111.904-.529.904-1.011V7.733c0-.813-.743-1.47-1.662-1.471h-2.668c-.23-.425-.717-.72-1.284-.72h-4.035c-.567 0-1.053.296-1.284.72H8.23c-.203-.737-.762-2.216-2.261-4.017A8.064 8.064 0 0 0 4.489.91 5.94 5.94 0 0 0 3.376.287 3.487 3.487 0 0 0 2.051 0 2.454 2.454 0 0 0 .86.297a1.61 1.61 0 0 0-.664.677A1.779 1.779 0 0 0 0 1.801c0 .34.086.68.259.998.172.316.436.61.799.825.818.483 1.589 1.076 2.155 1.67.32.332.564.665.73.968h-.019c-.917 0-1.66.658-1.66 1.47v1.064c0 .487.381.907.915 1.014l.267.053.076-.04a.78.78 0 0 1 .112.049.363.363 0 0 1 .128.1.31.31 0 0 1 .057.173v.001a.77.77 0 0 1-.068.245c-.095.239-.296.596-.544 1.022-.742 1.285-1.904 3.225-1.905 5.645 0 .157.004.316.015.477.13 2.033.929 4.141 2.613 5.762C5.61 24.92 8.183 26.006 11.65 26c3.468.006 6.04-1.08 7.722-2.703 1.684-1.62 2.482-3.73 2.614-5.762.01-.161.014-.32.014-.477 0-1.844-.677-3.409-1.325-4.618zm-1.012-4.707v.866c-.221.026-.471.1-.733.245-.048.026-.093.064-.14.096h-1.937V7.48h2.523c.159 0 .287.113.287.253zM1.82 2.61a.886.886 0 0 1-.324-.342.991.991 0 0 1-.122-.468.7.7 0 0 1 .07-.318.346.346 0 0 1 .14-.151.938.938 0 0 1 .466-.115c.206 0 .464.057.742.173.417.171.864.469 1.238.775.374.304.68.62.831.802.97 1.167 1.5 2.17 1.786 2.87.058.142.104.269.144.385H5.405c-.216-.6-.628-1.168-1.142-1.712-.67-.702-1.525-1.356-2.442-1.899zm1.818 5.986v-.864c0-.14.128-.253.285-.254H9.96v1.46H4.512c-.062-.041-.124-.088-.188-.12a2.03 2.03 0 0 0-.685-.222zm16.974 8.869c-.115 1.804-.83 3.64-2.253 5.006-1.425 1.363-3.554 2.305-6.71 2.31-3.154-.005-5.283-.947-6.709-2.31-1.423-1.365-2.137-3.202-2.251-5.006a6.281 6.281 0 0 1-.014-.408c0-1.575.582-2.958 1.193-4.096.305-.57.615-1.074.862-1.521.123-.225.231-.435.315-.643.082-.208.146-.414.147-.652l-.002-.083a1.497 1.497 0 0 0-.06-.312h4.83v2.925c0 .531.49.962 1.09.962.6 0 1.091-.43 1.091-.962v-.621c0-.531.49-.966 1.09-.966.602 0 1.092.435 1.092.966v1.838c0 .617.564 1.12 1.265 1.12.696 0 1.265-.503 1.265-1.12V9.751h1.318c-.03.1-.052.203-.06.31l-.003.085c.002.239.065.444.148.652.147.362.367.74.616 1.171.749 1.286 1.756 3.022 1.753 5.09 0 .134-.003.27-.012.407z"
Expand Down Expand Up @@ -382,6 +387,21 @@
</clipPath>
</defs>
</symbol>
<symbol id="error" viewBox="0 0 13 13">
<g clipPath="url(#a)">
<path
fill="#FD4545"
fillRule="evenodd"
d="M1.523 1.185c.24-.241.568-.377.909-.377h8.571a1.286 1.286 0 0 1 1.286 1.286v8.571a1.286 1.286 0 0 1-1.286 1.286H4.2l-3.377.844a.429.429 0 0 1-.51-.551l.834-2.505V2.094c0-.341.136-.668.377-.91zm5.789 2.019a.536.536 0 1 0-1.072 0V6.34a.536.536 0 1 0 1.072 0V3.204zM5.92 9.021a.855.855 0 1 1 1.71 0 .855.855 0 0 1-1.71 0z"
clipRule="evenodd"
/>
</g>
<defs>
<clipPath id="a">
<path fill="#fff" d="M0 0h12v12H0z" transform="translate(.29 .808)" />
</clipPath>
</defs>
</symbol>
</svg>
</div>
<div id="dialog-container"></div>
Expand Down
69 changes: 30 additions & 39 deletions src/components/Common/ImageUploader/ImageUploader.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import { Button, useToastActionContext } from '@fun-eat/design-system';
import { useToastActionContext } from '@fun-eat/design-system';
import type { ChangeEventHandler } from 'react';
import styled from 'styled-components';

import { container, deleteButton, image, imageWrapper, uploadInput, uploadLabel } from './imageUploader.css';
import SvgIcon from '../Svg/SvgIcon';
import Text from '../Text/Text';

import { IMAGE_MAX_SIZE } from '@/constants';
import { useEnterKeyDown } from '@/hooks/common';
import { vars } from '@/styles/theme.css';

interface ReviewImageUploaderProps {
previewImage: string;
Expand Down Expand Up @@ -32,45 +36,32 @@ const ImageUploader = ({ previewImage, uploadImage, deleteImage }: ReviewImageUp
};

return (
<>
{previewImage ? (
<PreviewImageWrapper>
<img src={previewImage} alt="업로드한 사진" width={200} />
<Button type="button" customWidth="80px" color="primary" weight="bold" variant="filled" onClick={deleteImage}>
삭제하기
</Button>
</PreviewImageWrapper>
) : (
<ImageUploadLabel tabIndex={0} onKeyDown={handleKeydown} aria-label="사진 업로드 버튼" aria-hidden>
+
<input ref={inputRef} type="file" accept="image/*" onChange={handleImageUpload} />
</ImageUploadLabel>
<div className={container}>
<label
className={previewImage ? uploadLabel.uploaded : uploadLabel.default}
tabIndex={0}
onKeyDown={handleKeydown}
aria-label="사진 업로드 버튼"
aria-hidden
>
<SvgIcon variant="picture" width={28} height={28} fill={vars.colors.gray2} />
{previewImage && (
<Text as="span" size="caption4" color="disabled" weight="medium">
사진 변경하기
</Text>
)}
<input className={uploadInput} ref={inputRef} type="file" accept="image/*" onChange={handleImageUpload} />
</label>
{previewImage && (
<div className={imageWrapper}>
<img src={previewImage} className={image} width={80} height={80} alt="업로드한 사진" />
<button type="button" className={deleteButton} onClick={deleteImage}>
<SvgIcon variant="close2" width={10} height={10} fill="none" stroke={vars.colors.white} />
</button>
</div>
)}
</>
</div>
);
};

export default ImageUploader;

const ImageUploadLabel = styled.label`
display: flex;
justify-content: center;
align-items: center;
width: 92px;
height: 95px;
border: 1px solid ${({ theme }) => theme.borderColors.disabled};
border-radius: ${({ theme }) => theme.borderRadius.xs};
background: ${({ theme }) => theme.colors.gray1};
cursor: pointer;
& > input {
display: none;
}
`;

const PreviewImageWrapper = styled.div`
display: flex;
flex-direction: column;
gap: 20px;
align-items: center;
`;
56 changes: 56 additions & 0 deletions src/components/Common/ImageUploader/imageUploader.css.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { vars } from '@/styles/theme.css';
import { style, styleVariants } from '@vanilla-extract/css';

export const container = style({
display: 'flex',
gap: 8,
});

export const uploadLabelBase = style({
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
width: 80,
height: 80,
borderRadius: 6,
cursor: 'pointer',
});

export const uploadLabel = styleVariants({
default: [uploadLabelBase, { backgroundColor: vars.colors.background.category }],
uploaded: [
uploadLabelBase,
{ backgroundColor: vars.colors.background.default, border: `1px solid ${vars.colors.border.default}` },
],
});

export const uploadInput = style({
display: 'none',
});

export const imageWrapper = style({
position: 'relative',
width: 80,
height: 80,
});

export const image = style({
objectFit: 'cover',
borderRadius: 6,
});

export const deleteButton = style({
position: 'absolute',
top: 4,
right: 4,
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
width: 18,
height: 18,
borderRadius: '50%',
backgroundColor: vars.colors.black,
opacity: 0.5,
cursor: 'pointer',
});
2 changes: 2 additions & 0 deletions src/components/Common/Svg/SvgIcon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export const SVG_ICON_VARIANTS = [
'star2',
'review2',
'check2',
'picture',
'recipe',
'list',
'member',
Expand Down Expand Up @@ -46,6 +47,7 @@ export const SVG_ICON_VARIANTS = [
'box',
'close2',
'disk',
'error',
] as const;
export type SvgIconVariant = (typeof SVG_ICON_VARIANTS)[number];

Expand Down
18 changes: 18 additions & 0 deletions src/components/Common/Svg/SvgSprite.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,9 @@ const SvgSprite = () => {
<symbol id="check2" viewBox="0 0 22 22">
<path d="M20.2 4.683a1.013 1.013 0 0 1 0 1.438L9.008 17.317a1.013 1.013 0 0 1-1.437 0L1.8 11.55a1.017 1.017 0 0 1 1.437-1.437l5.045 5.045L18.758 4.683a1.013 1.013 0 0 1 1.437 0h.005z" />
</symbol>
<symbol id="picture" viewBox="0 0 28 29">
<path d="M1.977 5.125v19.25h23.625V5.125H1.977zm1.687 1.75h20.25v12.168l-4.456-4.648-.606-.63-3.824 3.966-4.851-5.086-.607-.63-5.906 6.126V6.875zm16.875 1.75c-.933 0-1.687.783-1.687 1.75s.754 1.75 1.687 1.75 1.688-.783 1.688-1.75-.755-1.75-1.688-1.75zM9.57 14.505l7.752 8.12H3.664V20.63l5.906-6.125zm9.282 1.75l5.062 5.25v1.12h-4.192l-3.507-3.664 2.637-2.707z" />
</symbol>
<symbol id="recipe" viewBox="0 0 22 26">
<path d="M20.675 12.44c-.324-.604-.643-1.123-.868-1.533a5.058 5.058 0 0 1-.256-.516.808.808 0 0 1-.068-.244v-.005.001a.311.311 0 0 1 .054-.168.344.344 0 0 1 .116-.096.83.83 0 0 1 .135-.06l.096.045.25-.053c.53-.111.904-.529.904-1.011V7.733c0-.813-.743-1.47-1.662-1.471h-2.668c-.23-.425-.717-.72-1.284-.72h-4.035c-.567 0-1.053.296-1.284.72H8.23c-.203-.737-.762-2.216-2.261-4.017A8.064 8.064 0 0 0 4.489.91 5.94 5.94 0 0 0 3.376.287 3.487 3.487 0 0 0 2.051 0 2.454 2.454 0 0 0 .86.297a1.61 1.61 0 0 0-.664.677A1.779 1.779 0 0 0 0 1.801c0 .34.086.68.259.998.172.316.436.61.799.825.818.483 1.589 1.076 2.155 1.67.32.332.564.665.73.968h-.019c-.917 0-1.66.658-1.66 1.47v1.064c0 .487.381.907.915 1.014l.267.053.076-.04a.78.78 0 0 1 .112.049.363.363 0 0 1 .128.1.31.31 0 0 1 .057.173v.001a.77.77 0 0 1-.068.245c-.095.239-.296.596-.544 1.022-.742 1.285-1.904 3.225-1.905 5.645 0 .157.004.316.015.477.13 2.033.929 4.141 2.613 5.762C5.61 24.92 8.183 26.006 11.65 26c3.468.006 6.04-1.08 7.722-2.703 1.684-1.62 2.482-3.73 2.614-5.762.01-.161.014-.32.014-.477 0-1.844-.677-3.409-1.325-4.618zm-1.012-4.707v.866c-.221.026-.471.1-.733.245-.048.026-.093.064-.14.096h-1.937V7.48h2.523c.159 0 .287.113.287.253zM1.82 2.61a.886.886 0 0 1-.324-.342.991.991 0 0 1-.122-.468.7.7 0 0 1 .07-.318.346.346 0 0 1 .14-.151.938.938 0 0 1 .466-.115c.206 0 .464.057.742.173.417.171.864.469 1.238.775.374.304.68.62.831.802.97 1.167 1.5 2.17 1.786 2.87.058.142.104.269.144.385H5.405c-.216-.6-.628-1.168-1.142-1.712-.67-.702-1.525-1.356-2.442-1.899zm1.818 5.986v-.864c0-.14.128-.253.285-.254H9.96v1.46H4.512c-.062-.041-.124-.088-.188-.12a2.03 2.03 0 0 0-.685-.222zm16.974 8.869c-.115 1.804-.83 3.64-2.253 5.006-1.425 1.363-3.554 2.305-6.71 2.31-3.154-.005-5.283-.947-6.709-2.31-1.423-1.365-2.137-3.202-2.251-5.006a6.281 6.281 0 0 1-.014-.408c0-1.575.582-2.958 1.193-4.096.305-.57.615-1.074.862-1.521.123-.225.231-.435.315-.643.082-.208.146-.414.147-.652l-.002-.083a1.497 1.497 0 0 0-.06-.312h4.83v2.925c0 .531.49.962 1.09.962.6 0 1.091-.43 1.091-.962v-.621c0-.531.49-.966 1.09-.966.602 0 1.092.435 1.092.966v1.838c0 .617.564 1.12 1.265 1.12.696 0 1.265-.503 1.265-1.12V9.751h1.318c-.03.1-.052.203-.06.31l-.003.085c.002.239.065.444.148.652.147.362.367.74.616 1.171.749 1.286 1.756 3.022 1.753 5.09 0 .134-.003.27-.012.407z" />
</symbol>
Expand Down Expand Up @@ -344,6 +347,21 @@ const SvgSprite = () => {
</clipPath>
</defs>
</symbol>
<symbol id="error" viewBox="0 0 13 13">
<g clipPath="url(#a)">
<path
fill="#FD4545"
fillRule="evenodd"
d="M1.523 1.185c.24-.241.568-.377.909-.377h8.571a1.286 1.286 0 0 1 1.286 1.286v8.571a1.286 1.286 0 0 1-1.286 1.286H4.2l-3.377.844a.429.429 0 0 1-.51-.551l.834-2.505V2.094c0-.341.136-.668.377-.91zm5.789 2.019a.536.536 0 1 0-1.072 0V6.34a.536.536 0 1 0 1.072 0V3.204zM5.92 9.021a.855.855 0 1 1 1.71 0 .855.855 0 0 1-1.71 0z"
clipRule="evenodd"
/>
</g>
<defs>
<clipPath id="a">
<path fill="#fff" d="M0 0h12v12H0z" transform="translate(.29 .808)" />
</clipPath>
</defs>
</symbol>
</svg>
);
};
Expand Down
33 changes: 26 additions & 7 deletions src/components/Review/RebuyCheckbox/RebuyCheckbox.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
import { Checkbox } from '@fun-eat/design-system';
import type { ChangeEventHandler } from 'react';

import { check, checkbox, container } from './rebuyCheckbox.css';
import { itemTitle } from '../ReviewRegisterForm/reviewRegisterForm.css';

import { SvgIcon, Text } from '@/components/Common';
import { useEnterKeyDown } from '@/hooks/common';
import { useReviewFormActionContext } from '@/hooks/context';
import { vars } from '@/styles/theme.css';

interface RebuyCheckboxProps {
isRebuy: boolean;
}

const RebuyCheckbox = () => {
const RebuyCheckbox = ({ isRebuy }: RebuyCheckboxProps) => {
const { handleReviewFormValue } = useReviewFormActionContext();
const { inputRef, labelRef, handleKeydown } = useEnterKeyDown();

Expand All @@ -13,11 +21,22 @@ const RebuyCheckbox = () => {
};

return (
<p onKeyDown={handleKeydown}>
<Checkbox ref={labelRef} inputRef={inputRef} weight="bold" onChange={handleRebuy} tabIndex={0}>
재구매할 생각이 있으신가요?
</Checkbox>
</p>
<>
<h2 className={itemTitle} tabIndex={0}>
재구매 여부
</h2>
<p onKeyDown={handleKeydown}>
<label className={container} ref={labelRef}>
<input type="checkbox" className={checkbox} ref={inputRef} onChange={handleRebuy} tabIndex={0} />
<span className={isRebuy ? check.checked : check.default} aria-hidden>
<SvgIcon variant="check2" width={10} height={10} fill={isRebuy ? vars.colors.primary : vars.colors.white} />
</span>
<Text as="span" weight="semiBold">
재구매할 생각이 있어요
</Text>
</label>
</p>
</>
);
};

Expand Down
27 changes: 27 additions & 0 deletions src/components/Review/RebuyCheckbox/rebuyCheckbox.css.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { vars } from '@/styles/theme.css';
import { style, styleVariants } from '@vanilla-extract/css';

export const container = style({
display: 'inline-flex',
alignItems: 'center',
gap: 8,
cursor: 'pointer',
});

export const checkbox = style({
display: 'none',
});

const checkBase = style({
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
width: 17,
height: 17,
borderRadius: '50%',
});

export const check = styleVariants({
default: [checkBase, { backgroundColor: vars.colors.icon.light }],
checked: [checkBase, { backgroundColor: vars.colors.black }],
});
Loading

0 comments on commit 0c9edba

Please sign in to comment.