-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
* UI: #127 프로젝트 상태 radio input 컴포넌트 분리 * UI: #127 시작일, 종료일 컴포넌트 분리 * UI: #127 마크다운 컴포넌트 분리 * UI: #127 파일 DropZone 컴포넌트 분리 * UI: #127 마크다운 컴포넌트 props 변경 * UI: #127 시작일, 종료일 컴포넌트 props 변경 * UI: #127 프로젝트 상태 컴포넌트 props 변경 * UI: #127 시작일, 종료일 컴포넌트 props 변경 * UI: #127 마크다운 컴포넌트 props 변경
- Loading branch information
Showing
6 changed files
with
335 additions
and
206 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
import { GoPlusCircle } from 'react-icons/go'; | ||
import { IoMdCloseCircle } from 'react-icons/io'; | ||
import type { CustomFile } from '@/types/FileType'; | ||
|
||
type FileDropZoneProps = { | ||
id: string; | ||
label: string; | ||
files: CustomFile[]; | ||
onFileChange: (e: React.ChangeEvent<HTMLInputElement>) => void; | ||
onFileDrop: (e: React.DragEvent<HTMLElement>) => void; | ||
onFileDeleteClick: (fileId: string) => void; | ||
}; | ||
|
||
// ToDo: 파일 업로드 API 작업시 구조 다시 한 번 확인해보기 | ||
export default function FileDropZone({ | ||
id, | ||
label, | ||
files, | ||
onFileChange: handleFileChange, | ||
onFileDrop: handleFileDrop, | ||
onFileDeleteClick: handleFileDeleteClick, | ||
}: FileDropZoneProps) { | ||
return ( | ||
<label htmlFor={id}> | ||
<h3 className="text-large">{label}</h3> | ||
<input type="file" id={id} className="h-0 w-0 opacity-0" multiple hidden onChange={handleFileChange} /> | ||
<section | ||
className="flex cursor-pointer items-center gap-4 rounded-sl border-2 border-dashed border-input p-10" | ||
onDrop={handleFileDrop} | ||
> | ||
<ul className="flex grow flex-wrap gap-4"> | ||
{files.map(({ id, file }) => ( | ||
<li key={id} className="flex items-center gap-4 rounded-md bg-button px-4 py-2"> | ||
<span>{file.name}</span> | ||
<IoMdCloseCircle | ||
className="text-close" | ||
onClick={(e: React.MouseEvent<HTMLOrSVGElement>) => { | ||
e.preventDefault(); | ||
handleFileDeleteClick(id); | ||
}} | ||
/> | ||
</li> | ||
))} | ||
</ul> | ||
<div> | ||
<GoPlusCircle className="size-15 text-[#5E5E5E]" /> | ||
</div> | ||
</section> | ||
</label> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
import { useState } from 'react'; | ||
import { useFormContext } from 'react-hook-form'; | ||
import ToggleButton from '@components/common/ToggleButton'; | ||
import CustomMarkdown from '@components/common/CustomMarkdown'; | ||
|
||
type MarkdownEditorProps = { | ||
id: string; | ||
label: string; | ||
contentFieldName: string; | ||
}; | ||
|
||
export default function MarkdownEditor({ id, label, contentFieldName }: MarkdownEditorProps) { | ||
const [preview, setPreview] = useState(false); | ||
const { watch, register } = useFormContext(); | ||
|
||
const handlePreviewToggle = () => setPreview((prev) => !prev); | ||
|
||
return ( | ||
<label htmlFor={id} className="mb-20"> | ||
<h3 className="flex items-center space-x-2"> | ||
<span className="text-large">{label}</span> | ||
<ToggleButton id="preview" checked={preview} onChange={handlePreviewToggle} /> | ||
</h3> | ||
{preview ? ( | ||
<CustomMarkdown markdown={watch(contentFieldName)} /> | ||
) : ( | ||
<textarea | ||
id={id} | ||
rows={10} | ||
className="w-full border border-input p-10 placeholder:text-xs" | ||
placeholder="마크다운 형식으로 입력해주세요." | ||
{...register(contentFieldName)} | ||
/> | ||
)} | ||
</label> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
import { useState } from 'react'; | ||
import { useFormContext } from 'react-hook-form'; | ||
import { TASK_VALIDATION_RULES } from '@constants/formValidationRules'; | ||
import ToggleButton from '@components/common/ToggleButton'; | ||
|
||
import type { FieldError } from 'react-hook-form'; | ||
import type { Project } from '@/types/ProjectType'; | ||
|
||
type PeriodDateInputProps = { | ||
startDateLabel: string; | ||
endDateLabel: string; | ||
startDateId: string; | ||
endDateId: string; | ||
startDate: Project['startDate']; | ||
endDate: Project['endDate']; | ||
startDateFieldName: string; | ||
endDateFieldName: string; | ||
}; | ||
|
||
export default function PeriodDateInput({ | ||
startDateLabel, | ||
endDateLabel, | ||
startDateId, | ||
endDateId, | ||
startDate, | ||
endDate, | ||
startDateFieldName, | ||
endDateFieldName, | ||
}: PeriodDateInputProps) { | ||
const [hasDeadline, setHasDeadline] = useState(false); | ||
const { | ||
setValue, | ||
getValues, | ||
clearErrors, | ||
watch, | ||
register, | ||
formState: { errors }, | ||
} = useFormContext(); | ||
|
||
const handleDeadlineToggle = () => { | ||
setValue(endDateFieldName, getValues(startDateFieldName)); | ||
clearErrors(endDateFieldName); | ||
setHasDeadline((prev) => !prev); | ||
}; | ||
|
||
return ( | ||
<div className="flex items-center justify-center gap-10"> | ||
<label htmlFor={startDateId} className="w-1/2"> | ||
<h3 className="text-large">{startDateLabel}</h3> | ||
<input | ||
id={startDateId} | ||
type="date" | ||
{...register(startDateFieldName, { | ||
...TASK_VALIDATION_RULES.START_DATE(startDate, endDate), | ||
onChange: (e) => { | ||
if (!hasDeadline) setValue(endDateFieldName, e.target.value); | ||
}, | ||
})} | ||
/> | ||
<div className={`my-5 h-10 grow text-xs text-error ${errors[startDateFieldName] ? 'visible' : 'invisible'}`}> | ||
{(errors[startDateFieldName] as FieldError | undefined)?.message} | ||
</div> | ||
</label> | ||
<label htmlFor={endDateId} className="w-1/2"> | ||
<h3 className="flex items-center space-x-2 text-large"> | ||
<span>{endDateLabel}</span> | ||
<ToggleButton id="deadline" checked={hasDeadline} onChange={handleDeadlineToggle} /> | ||
</h3> | ||
<input | ||
id={endDateId} | ||
type="date" | ||
className={`${hasDeadline ? '' : '!bg-disable'}`} | ||
disabled={!hasDeadline} | ||
{...register( | ||
endDateFieldName, | ||
TASK_VALIDATION_RULES.END_DATE(hasDeadline, startDate, endDate, watch(startDateFieldName)), | ||
)} | ||
/> | ||
<div className={`my-5 h-10 grow text-xs text-error ${errors[endDateFieldName] ? 'visible' : 'invisible'}`}> | ||
{(errors[endDateFieldName] as FieldError | undefined)?.message} | ||
</div> | ||
</label> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
import { useFormContext } from 'react-hook-form'; | ||
import { TASK_VALIDATION_RULES } from '@constants/formValidationRules'; | ||
|
||
import type { FieldError } from 'react-hook-form'; | ||
import type { ProjectStatus } from '@/types/ProjectStatusType'; | ||
|
||
type StatusRadioProps = { | ||
statusFieldName: string; | ||
statusList: ProjectStatus[]; | ||
}; | ||
|
||
export default function StatusRadio({ statusFieldName, statusList }: StatusRadioProps) { | ||
const { | ||
watch, | ||
register, | ||
formState: { errors }, | ||
} = useFormContext(); | ||
|
||
return ( | ||
<> | ||
{/* ToDo: 상태 선택 리팩토링 할 것 */} | ||
<div className="flex items-center justify-start gap-4"> | ||
{statusList.map((status) => { | ||
const { statusId, statusName, colorCode } = status; | ||
const isChecked = Number(watch('statusId')) === statusId; | ||
return ( | ||
<label | ||
key={statusId} | ||
htmlFor={statusName} | ||
className={`flex cursor-pointer items-center rounded-lg border px-5 py-3 text-emphasis ${isChecked ? 'border-input bg-white' : 'bg-button'}`} | ||
> | ||
<input | ||
id={statusName} | ||
type="radio" | ||
className="invisible h-0 w-0" | ||
value={statusId} | ||
checked={isChecked} | ||
{...register(statusFieldName, TASK_VALIDATION_RULES.STATUS)} | ||
/> | ||
<div style={{ borderColor: colorCode }} className="mr-3 h-8 w-8 rounded-full border" /> | ||
<h3 className="text-xs">{statusName}</h3> | ||
</label> | ||
); | ||
})} | ||
</div> | ||
<div className={`my-5 h-10 grow text-xs text-error ${errors[statusFieldName] ? 'visible' : 'invisible'}`}> | ||
{(errors[statusFieldName] as FieldError | undefined)?.message} | ||
</div> | ||
</> | ||
); | ||
} |
Oops, something went wrong.