Skip to content

Commit

Permalink
HOTFIX: 리스트 생성 오류 수정 (#67)
Browse files Browse the repository at this point in the history
* Fix: 리스트 생성  라벨 onChange마다 에러 설정되는 함수 수정

* Fix: 리스트 생성 동일한 key사용 이슈 해결

* Design: 토스트 메시지  위쪽으로 위치변경

* Chore: 모바일 환경에서 확대되는 현상 막기

* Design: 버튼 클릭시 생기는 박스 없애기 시도
  • Loading branch information
seoyoung-min authored Feb 24, 2024
1 parent 637bb79 commit 33b482c
Show file tree
Hide file tree
Showing 9 changed files with 178 additions and 155 deletions.
7 changes: 1 addition & 6 deletions src/app/layout.css.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,4 @@ export const body = style({
backgroundColor: vars.color.white,
});

export const toastContainer = style([
fonts.labelMedium,
{
marginBottom: 50,
},
]);
export const toastContainer = style([fonts.labelMedium]);
5 changes: 5 additions & 0 deletions src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ export default function TempLayout({ children }: { children: ReactNode }) {
<html lang="ko">
<head>
<title>ListyWave</title>
<meta
name="viewport"
content="width=device-width, initial-scale=1, maximum-scale=1.0,
user-scalable=0"
/>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/swiper@9/swiper-bundle.min.css" />
<Script
src="https://t1.kakaocdn.net/kakao_js_sdk/2.6.0/kakao.min.js"
Expand Down
15 changes: 11 additions & 4 deletions src/app/list/create/_components/CreateList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,15 @@ function CreateList({ onNextClick, type }: CreateListProps) {
handleQueryParams();
}, []);

const isValid =
title &&
category &&
!errors.title &&
!errors.category &&
!errors.labels &&
!errors.collaboratorIds &&
!errors.description;

return (
<div>
{/* 헤더 */}
Expand All @@ -92,10 +101,8 @@ function CreateList({ onNextClick, type }: CreateListProps) {
}}
right={
<button
className={title && category ? styles.headerNextButtonActive : styles.headerNextButton}
disabled={
!!errors.title || !!errors.category || !!errors.labels || !!errors.collaboratorIds || !!errors.description
}
className={isValid ? styles.headerNextButtonActive : styles.headerNextButton}
disabled={!isValid}
onClick={onNextClick}
>
다음
Expand Down
10 changes: 10 additions & 0 deletions src/app/list/create/_components/item/ItemLayout.css.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { style } from '@vanilla-extract/css';
import { vars } from '@/styles/theme.css';
import * as fonts from '@/styles/font.css';

export const itemHeader = style({
width: '100%',
Expand All @@ -13,6 +14,15 @@ export const itemHeader = style({
overflow: 'hidden',
});

export const titleError = style([
fonts.bodySmall,
{
marginBottom: '4px',
flexShrink: '0',
color: vars.color.red,
},
]);

export const headerIcon = style({
flexShrink: '0',
});
Expand Down
67 changes: 36 additions & 31 deletions src/app/list/create/_components/item/ItemLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ interface ItemLayoutProps {
handleDeleteItem: () => void;
itemLength: number;
titleInput: ReactNode;
titleErrorMessage?: string;
commentTextArea: ReactNode;
commentLength: ReactNode;
linkModal: ReactNode;
Expand All @@ -29,6 +30,7 @@ export default function ItemLayout({
handleDeleteItem,
itemLength,
titleInput,
titleErrorMessage,
commentTextArea,
commentLength,
linkModal,
Expand All @@ -38,40 +40,43 @@ export default function ItemLayout({
handleImageAdd,
}: ItemLayoutProps) {
return (
<Accordion>
<AccordionSummary>
<div className={styles.itemHeader}>
<DndIcon width="18" height="18" alt="드래그앤드롭" className={styles.headerIcon} />
<div className={styles.rankAndTitle}>
<Label colorType={index === 0 ? 'blue' : 'skyblue'}>{`${index + 1}위`}</Label>
{titleInput}
</div>
</div>
</AccordionSummary>
<AccordionDetails className={styles.details}>
<div className={styles.moreInfo}>
{commentTextArea}
<div className={styles.countLength}>{commentLength}</div>
<div className={styles.toolbar}>
<div className={styles.fileButtons}>
{linkModal}
<ImageUploader index={index} handleImageAdd={handleImageAdd}>
{imageInput}
</ImageUploader>
<div>
{titleErrorMessage && <p className={styles.titleError}> {titleErrorMessage}</p>}
<Accordion>
<AccordionSummary>
<div className={styles.itemHeader}>
<DndIcon width="18" height="18" alt="드래그앤드롭" className={styles.headerIcon} />
<div className={styles.rankAndTitle}>
<Label colorType={index === 0 ? 'blue' : 'skyblue'}>{`${index + 1}위`}</Label>
{titleInput}
</div>
{itemLength > MIN_ITEM_COUNT && (
<button onClick={handleDeleteItem}>
<DeleteIcon fill={vars.color.gray9} alt="아이템 삭제" />
</button>
)}
</div>
</AccordionSummary>
<AccordionDetails className={styles.details}>
<div className={styles.moreInfo}>
{commentTextArea}
<div className={styles.countLength}>{commentLength}</div>
<div className={styles.toolbar}>
<div className={styles.fileButtons}>
{linkModal}
<ImageUploader index={index} handleImageAdd={handleImageAdd}>
{imageInput}
</ImageUploader>
</div>
{itemLength > MIN_ITEM_COUNT && (
<button onClick={handleDeleteItem}>
<DeleteIcon fill={vars.color.gray9} alt="아이템 삭제" />
</button>
)}
</div>

<div className={styles.previewContainer}>
{linkPreview}
{imagePreview}
<div className={styles.previewContainer}>
{linkPreview}
{imagePreview}
</div>
</div>
</div>
</AccordionDetails>
</Accordion>
</AccordionDetails>
</Accordion>
</div>
);
}
221 changes: 111 additions & 110 deletions src/app/list/create/_components/item/Items.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -124,119 +124,120 @@ export default function Items({ type, setItemChanged }: ItemsProps) {

const imageRegister = register(`items.${index}.imageUrl`);
return (
<div key={item.id}>
{titleError?.type !== 'required' && (
<p key={item.id} className={styles.error}>
{titleError?.message}
</p>
)}
<Draggable key={item.id} draggableId={item.id} index={index}>
{(provided, snapshot) => (
<div
className={snapshot.isDragging ? styles.draggingItem : styles.item}
key={item.id}
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
>
<ItemLayout
index={index}
handleDeleteItem={() => {
handleDeleteItem(index);
}}
itemLength={watchItems.length}
titleInput={
<input
className={titleError ? styles.errorTitle : styles.title}
placeholder={itemPlaceholder.title}
autoComplete="off"
maxLength={101}
{...register(`items.${index}.title`, itemTitleRules)}
readOnly={
type === 'edit' &&
listDetailData?.items.some((item) => item.id === getValues(`items.${index}.id`))
}
/>
}
commentTextArea={
<textarea
className={styles.comment}
placeholder={itemPlaceholder.comment}
rows={3}
maxLength={101}
{...register(`items.${index}.comment`, itemCommentRules)}
/>
}
commentLength={
<p className={commentError ? styles.errorCountLength : styles.countLength}>
{watchItems[index]?.comment?.length ?? 0}/100
</p>
}
linkModal={
<LinkModal
onCancelButtonClick={() => {
handleLinkModalCancel(index);
}}
onTriggerButtonClick={() => {
handleLinkModalOpen(index);
}}
onConfirmButtonClick={() => {
handleLinkModalConfirm(index);
<Draggable key={item.id} draggableId={item.id} index={index}>
{(provided, snapshot) => (
<div
className={snapshot.isDragging ? styles.draggingItem : styles.item}
key={item.id}
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
>
{/* {titleError?.type !== 'required' && (
<p key={item.id} className={styles.itemTitleError}>
{titleError?.message}
</p>
)} */}
<ItemLayout
index={index}
handleDeleteItem={() => {
handleDeleteItem(index);
}}
itemLength={watchItems.length}
titleErrorMessage={
titleError && titleError?.type !== 'required' ? titleError?.message : undefined
}
titleInput={
<input
className={titleError ? styles.errorTitle : styles.title}
placeholder={itemPlaceholder.title}
autoComplete="off"
maxLength={101}
{...register(`items.${index}.title`, itemTitleRules)}
readOnly={
type === 'edit' &&
listDetailData?.items.some((item) => item.id === getValues(`items.${index}.id`))
}
/>
}
commentTextArea={
<textarea
className={styles.comment}
placeholder={itemPlaceholder.comment}
rows={3}
maxLength={101}
{...register(`items.${index}.comment`, itemCommentRules)}
/>
}
commentLength={
<p className={commentError ? styles.errorCountLength : styles.countLength}>
{watchItems[index]?.comment?.length ?? 0}/100
</p>
}
linkModal={
<LinkModal
onCancelButtonClick={() => {
handleLinkModalCancel(index);
}}
onTriggerButtonClick={() => {
handleLinkModalOpen(index);
}}
onConfirmButtonClick={() => {
handleLinkModalConfirm(index);
}}
isLinkValid={!linkError && watchItems[index]?.link?.length !== 0}
>
<div className={styles.linkModalChildren}>
<input
className={styles.linkInput}
type="url"
placeholder={itemPlaceholder.link}
autoComplete="off"
{...register(`items.${index}.link`, itemLinkRules)}
/>
{watchItems[index]?.link?.length !== 0 && linkError && (
<p className={styles.error}>{linkError.message}</p>
)}
</div>
</LinkModal>
}
imageInput={
<input
className={styles.imageInput}
type="file"
accept=".jpg, .jpeg, .png"
id={`${index}-image`}
{...imageRegister}
onChange={(e) => {
handleImageChange(e, imageRegister);
}}
/>
}
linkPreview={
watchItems[index]?.link && (
<LinkPreview
url={watchItems[index].link}
handleClearButtonClick={() => {
setValue(`items.${index}.link`, '');
}}
isLinkValid={!linkError && watchItems[index]?.link?.length !== 0}
>
<div className={styles.linkModalChildren}>
<input
className={styles.linkInput}
type="url"
placeholder={itemPlaceholder.link}
autoComplete="off"
{...register(`items.${index}.link`, itemLinkRules)}
/>
{watchItems[index]?.link?.length !== 0 && linkError && (
<p className={styles.error}>{linkError.message}</p>
)}
</div>
</LinkModal>
}
imageInput={
<input
className={styles.imageInput}
type="file"
accept=".jpg, .jpeg, .png"
id={`${index}-image`}
{...imageRegister}
onChange={(e) => {
handleImageChange(e, imageRegister);
/>
)
}
imagePreview={
watchItems[index]?.imageUrl !== '' && (
<ImagePreview
image={watchItems[index]?.imageUrl}
handleClearButtonClick={() => {
setValue(`items.${index}.imageUrl`, '');
}}
/>
}
linkPreview={
watchItems[index]?.link && (
<LinkPreview
url={watchItems[index].link}
handleClearButtonClick={() => {
setValue(`items.${index}.link`, '');
}}
/>
)
}
imagePreview={
watchItems[index]?.imageUrl !== '' && (
<ImagePreview
image={watchItems[index]?.imageUrl}
handleClearButtonClick={() => {
setValue(`items.${index}.imageUrl`, '');
}}
/>
)
}
handleImageAdd={setItemChanged}
/>
</div>
)}
</Draggable>
</div>
)
}
handleImageAdd={setItemChanged}
/>
</div>
)}
</Draggable>
);
})}
{provided.placeholder}
Expand Down
Loading

0 comments on commit 33b482c

Please sign in to comment.