-
Notifications
You must be signed in to change notification settings - Fork 31
[최권진] sprint6 #126
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
The head ref may contain hidden characters: "React-\uCD5C\uAD8C\uC9C4"
[최권진] sprint6 #126
Changes from all commits
354e76b
0288da1
cd27ba2
8a9ae24
5565b4e
2f97876
40808a2
05d13f4
870cb37
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,23 +1,25 @@ | ||
| # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. | ||
|
|
||
| # dependencies | ||
| /node_modules | ||
| /.pnp | ||
| .pnp.js | ||
|
|
||
| # testing | ||
| /coverage | ||
| # Logs | ||
| logs | ||
| *.log | ||
| npm-debug.log* | ||
| yarn-debug.log* | ||
| yarn-error.log* | ||
| pnpm-debug.log* | ||
| lerna-debug.log* | ||
|
|
||
| # production | ||
| /build | ||
| node_modules | ||
| dist | ||
| dist-ssr | ||
| *.local | ||
|
|
||
| # misc | ||
| # Editor directories and files | ||
| .vscode/* | ||
| !.vscode/extensions.json | ||
| .idea | ||
| .DS_Store | ||
| .env.local | ||
| .env.development.local | ||
| .env.test.local | ||
| .env.production.local | ||
| *.suo | ||
| *.ntvs* | ||
| *.njsproj | ||
| *.sln | ||
| *.sw? | ||
|
|
||
| npm-debug.log* | ||
| yarn-debug.log* | ||
| yarn-error.log* |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| import React from "react"; | ||
| import FormInput from "../Common/FormInput"; | ||
|
|
||
| const FormField = ({ label, id, type = "text", placeholder, register }) => { | ||
| return ( | ||
| <div className="mb-[24px]"> | ||
| <label | ||
| htmlFor={id} | ||
| className="font-[700] text-[18px] text-[#1F2937] block mb-[8px]" | ||
| > | ||
| {label} | ||
| </label> | ||
| <FormInput id={id} type={type} placeholder={placeholder} {...register} /> | ||
| </div> | ||
| ); | ||
| }; | ||
|
|
||
| export default FormField; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| const FormHeader = ({ title, isSubmitEnabled }) => { | ||
| return ( | ||
| <div> | ||
| <div className="flex justify-between "> | ||
| <div className="text-[20px] font-[700] text-[#1F2937]">{title}</div> | ||
| <button | ||
| type="submit" | ||
| disabled={!isSubmitEnabled} | ||
| className={`rounded-[8px] w-[74px] h-[42px] text-[#FFFFFF] ${ | ||
| isSubmitEnabled ? "bg-[#3692FF]" : "bg-[#9CA3AF] cursor-not-allowed" | ||
| }`} | ||
| > | ||
| 등록 | ||
| </button> | ||
| </div> | ||
| </div> | ||
| ); | ||
| }; | ||
|
|
||
| export default FormHeader; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,77 @@ | ||
| import addItem from "../../assets/add-item.png"; | ||
| import xIcon from "../../assets/x-icon.png"; | ||
|
|
||
| import FormInput from "../Common/FormInput"; | ||
|
|
||
| const ImageUploader = ({ | ||
| previewUrl, | ||
| setPreviewUrl, | ||
| showWarning, | ||
| setShowWarning, | ||
| imageInputRef, | ||
| }) => { | ||
| const handleUploadClick = () => { | ||
| imageInputRef.current.click(); | ||
| }; | ||
|
|
||
| const handleFileChange = (e) => { | ||
| const file = e.target.files[0]; | ||
| if (previewUrl) { | ||
| setShowWarning(true); | ||
| } else if (file) { | ||
| const reader = new FileReader(); | ||
| reader.onloadend = () => { | ||
| setPreviewUrl(reader.result); | ||
| setShowWarning(false); | ||
| }; | ||
| reader.readAsDataURL(file); | ||
| } | ||
| e.target.value = ""; | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 해당 부분은 이미지를 지우거나, 제출을 완료할 때 들어가야 됩니다~! 지금은 previewUrl만 남고 실제 제출할 데이터는 남지 않게 되네요 🤣 |
||
| }; | ||
| return ( | ||
| <div> | ||
| <div className="font-[700] text-[18px] text-[#1F2937] mt-[24px]"> | ||
| 상품 이미지 | ||
| </div> | ||
| <div className="flex gap-[10px] "> | ||
| <FormInput | ||
| type="file" | ||
| accept="image/*" | ||
| hidden | ||
| ref={imageInputRef} | ||
| onChange={handleFileChange} | ||
| /> | ||
| <button | ||
| onClick={handleUploadClick} | ||
| className="w-[168px] h-[168px] bg-[#F3F4F6] flex justify-center items-center rounded-[12px] pc:w-[282px] pc:h-[282px] " | ||
| > | ||
| <img src={addItem} alt="addItem" className="w-[74px] h-[86px]" /> | ||
| </button> | ||
| <div className="w-[168px] h-[168px] relative pc:w-[282px] pc:h-[282px] "> | ||
| {previewUrl && ( | ||
| <> | ||
| <img | ||
| src={previewUrl} | ||
| alt="미리보기" | ||
| className="w-[168px] h-[168px] object-cover rounded-[12px] pc:w-[282px] pc:h-[282px]" | ||
| /> | ||
| <img | ||
| src={xIcon} | ||
| alt="image delete button" | ||
| className="w-[22px] h-[24px] absolute top-[12px] right-[12px]" | ||
| onClick={() => setPreviewUrl("")} | ||
| /> | ||
| </> | ||
| )} | ||
| </div> | ||
| </div> | ||
| {showWarning && ( | ||
| <div className="text-red-500 text-sm mt-[16px]"> | ||
| *이미지 등록은 최대 1개까지 가능합니다. | ||
| </div> | ||
| )} | ||
| </div> | ||
| ); | ||
| }; | ||
|
|
||
| export default ImageUploader; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,56 @@ | ||
| import { useState } from "react"; | ||
|
|
||
| const TagInput = ({ tags, setTags }) => { | ||
| const [tagInput, setTagInput] = useState(""); | ||
|
|
||
| const handleKeyDown = (e) => { | ||
| if (e.nativeEvent.isComposing) return; | ||
|
|
||
| if (e.key === "Enter") { | ||
| e.preventDefault(); | ||
| const trimmed = tagInput.trim(); | ||
| if (trimmed && !tags.includes(trimmed)) { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 중복을 걸러낼 때 Set을 써보셔도 좋아요~! :) |
||
| setTags([...tags, trimmed]); | ||
| } | ||
| setTagInput(""); | ||
| } | ||
| }; | ||
| const removeTag = (index) => { | ||
| setTags(tags.filter((_, i) => i !== index)); | ||
| }; | ||
| return ( | ||
| <> | ||
| <label htmlFor="tags" className="font-[700] text-[18px] text-[#1F2937]"> | ||
| 태그 | ||
| </label> | ||
| <input | ||
| type="text" | ||
| value={tagInput} | ||
| onChange={(e) => setTagInput(e.target.value)} | ||
| onKeyDown={handleKeyDown} | ||
| placeholder="태그를 입력해주세요" | ||
| className="input-primary" | ||
| /> | ||
|
|
||
| <div className="flex flex-wrap gap-2 mt-2"> | ||
| {tags.map((tag, i) => ( | ||
| <div | ||
| key={i} | ||
| className="bg-gray-100 rounded-full px-4 py-2 flex items-center gap-2 mb-[60px]" | ||
| > | ||
| <span className="text-[#1F2937]">#{tag}</span> | ||
| <button | ||
| type="button" | ||
| onClick={() => removeTag(i)} | ||
| className="w-5 h-5 flex items-center justify-center rounded-full bg-gray-400 text-white" | ||
| > | ||
| x | ||
| </button> | ||
| </div> | ||
| ))} | ||
| </div> | ||
| </> | ||
| ); | ||
| }; | ||
|
|
||
| export default TagInput; | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
arbitrary values를 줄여보시는 것도 좋아요 :)
[https://fe-developers.kakaoent.com/2022/220303-tailwind-tips/#arbitrary-values-줄이기](https://fe-developers.kakaoent.com/2022/220303-tailwind-tips/#arbitrary-values-%EC%A4%84%EC%9D%B4%EA%B8%B0)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
또한 기본적인 디자인 토큰은 tailwind config에 넣어서 쓰시는 것이 좋습니다~!