-
Notifications
You must be signed in to change notification settings - Fork 39
[문주영] Sprint9 #227
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: "Next-\uBB38\uC8FC\uC601-sprint9"
[문주영] Sprint9 #227
Changes from all commits
e20296e
c867e85
579aaba
c71f3d1
f1fdbb0
4d5f610
ea0349a
95ca172
385a904
a3191c6
44da60d
f30b467
74bb553
f044260
75c9fd0
6717259
3e6594b
40d431b
0b4fb70
c4f423e
c554931
3ded6cb
aadd37f
9697015
938c91f
fb783fc
3c025f7
a76152a
fa30291
26020d6
1b146f8
939718c
b698315
044ce1f
46440e0
e151c90
c247aae
85fa813
b174173
4020475
1923ce7
31d1efe
698c608
f407bfd
fd9eb58
93f35af
d15717d
6ab3546
21ea9e7
5eb0ebf
d6c2e50
185848a
1ca3b9d
3000359
3194f9a
023cea0
ed34fef
50a12be
3cbf9f0
36304ca
ae74e76
f2e818b
734ec8a
55e32e4
f366683
4066164
cd888c7
c6939fd
be5129b
366f91e
3f74429
bba28a8
b30d4f1
9130eba
ba0b78e
b69225e
0d5284a
7303a2e
5fa190f
f809b1f
1f41f2b
ef9d6b7
b84166e
a0d53e7
f2a38c9
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 |
|---|---|---|
|
|
@@ -33,3 +33,6 @@ yarn-error.log* | |
| # typescript | ||
| *.tsbuildinfo | ||
| next-env.d.ts | ||
|
|
||
| # vscode | ||
| /.vscode | ||
|
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. 💊 제안 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,77 @@ | ||
| button.btn { | ||
| position: relative; | ||
| height: 56px; | ||
| } | ||
|
|
||
| button.btn.large { | ||
| width: 168px; | ||
| } | ||
|
|
||
| button.btn.small { | ||
| width: 56px; | ||
| } | ||
|
|
||
| button.btn div { | ||
| position: absolute; | ||
| height: 52px; | ||
| border: 2px solid var(--slate900); | ||
| border-radius: 24px; | ||
| } | ||
|
|
||
| button.btn.large div { | ||
| width: 164.35px; | ||
| } | ||
|
|
||
| button.btn.small div { | ||
| width: 54.78px; | ||
| } | ||
|
|
||
| button.btn div.content { | ||
| top: 0; | ||
| left: 0; | ||
| display: flex; | ||
| background-color: var(--slate200); | ||
| color: var(--slate900); | ||
| line-height: 18px; | ||
| font-size: 16px; | ||
| font-weight: 700; | ||
| } | ||
|
|
||
| button.btn div.content.delete { | ||
| background-color: var(--rose500); | ||
| color: white; | ||
| } | ||
|
|
||
| button.btn:hover div.content.add { | ||
| background-color: var(--violet600); | ||
| color: white; | ||
| } | ||
|
|
||
| button.btn:hover div.content.edit { | ||
| background-color: var(--lime300); | ||
| } | ||
|
|
||
| button.btn.large div.content { | ||
| align-items: center; | ||
| gap: 4px; | ||
| justify-content: center; | ||
| } | ||
|
|
||
| button.btn.small div.content { | ||
| padding: 16px; | ||
| } | ||
|
|
||
| button.btn div.content.add img#light, | ||
| button.btn:hover div.content.add img#dark { | ||
| display: none; | ||
| } | ||
|
|
||
| button.btn:hover div.content.add img#light { | ||
| display: initial; | ||
| } | ||
|
|
||
| button.btn div.shadow { | ||
| bottom: 0; | ||
| right: 0; | ||
| background-color: var(--slate900); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,55 @@ | ||
| import { ButtonHTMLAttributes, useMemo } from "react"; | ||
| import Image from "next/image"; | ||
| import ic_plus_black from "@/assets/icons/plus_black.svg"; | ||
| import ic_plus_white from "@/assets/icons/plus_white.svg"; | ||
| import ic_cross from "@/assets/icons/cross.svg"; | ||
| import ic_check from "@/assets/icons/check.svg"; | ||
| import styles from "./Btn.module.css"; | ||
Moon-ju-young marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| interface Props extends ButtonHTMLAttributes<HTMLButtonElement> { | ||
|
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. 💬 여담 물론 아래처럼 export 되지 않은 prop를 참조할 수 있습니다. type BtnProps = ComponentProps<typeof Btn>; |
||
| size?: "large" | "small"; | ||
| mode: "add" | "delete" | "edit"; | ||
| } | ||
|
|
||
| const CONTENT: Record<Props["mode"], string> = { | ||
| add: "추가하기", | ||
| delete: "삭제하기", | ||
| edit: "수정 완료", | ||
| }; | ||
|
|
||
| export default function Btn({ | ||
| className = "", | ||
| size = "large", | ||
| mode, | ||
| ...props | ||
| }: Props) { | ||
| const img = useMemo(() => { | ||
| switch (mode) { | ||
| case "add": | ||
| return { alt: "plus", src: ic_plus_black }; | ||
| case "delete": | ||
| return { alt: "cross", src: ic_cross }; | ||
| case "edit": | ||
| return { alt: "check", src: ic_check }; | ||
| } | ||
| }, [mode]); | ||
|
|
||
| return ( | ||
| <button className={`${styles.btn} ${styles[size]} ${className}`} {...props}> | ||
Moon-ju-young marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| <div className={styles.shadow}></div> | ||
| <div className={`${styles.content} ${styles[mode]}`}> | ||
| <Image height={16} width={16} id={styles.dark} {...img} /> | ||
| {mode === "add" && ( | ||
| <Image | ||
| height={16} | ||
| width={16} | ||
| id={styles.light} | ||
| alt="plus" | ||
| src={ic_plus_white} | ||
| /> | ||
| )} | ||
| {size === "large" && CONTENT[mode]} | ||
| </div> | ||
| </button> | ||
| ); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| .check-list { | ||
| display: flex; | ||
| align-items: center; | ||
| gap: 16px; | ||
| height: 50px; | ||
| padding: 0 12px; | ||
| border: 2px solid var(--slate900); | ||
| border-radius: 9999px; | ||
| color: var(--slate800); | ||
| line-height: 18px; | ||
| font-size: 16px; | ||
| font-weight: 400; | ||
| } | ||
|
|
||
| .check-list.true { | ||
| background-color: var(--violet100); | ||
| text-decoration: line-through 1px; | ||
| } | ||
|
|
||
| .check-list.false { | ||
| background-color: white; | ||
| } | ||
|
|
||
| .check-list button { | ||
| line-height: 0; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| import { HTMLAttributes, MouseEventHandler } from "react"; | ||
| import Image from "next/image"; | ||
| import ic_checked from "@/assets/icons/checkbox_checked.svg"; | ||
| import ic_empty from "@/assets/icons//checkbox_empty.svg"; | ||
| import styles from "./CheckList.module.css"; | ||
|
|
||
| interface Props extends HTMLAttributes<HTMLDivElement> { | ||
| isChecked: boolean; | ||
| onButtonClick: MouseEventHandler<HTMLButtonElement>; | ||
| } | ||
|
|
||
| export default function CheckList({ | ||
| className = "", | ||
| children, | ||
| isChecked, | ||
| onButtonClick, | ||
| ...props | ||
| }: Props) { | ||
| return ( | ||
| <div | ||
| className={`${styles["check-list"]} ${ | ||
| styles[String(isChecked)] | ||
| } ${className}`} | ||
| {...props} | ||
| > | ||
| <button type="button" onClick={onButtonClick}> | ||
| <Image alt="checkbox" src={isChecked ? ic_checked : ic_empty} /> | ||
| </button> | ||
| {children} | ||
| </div> | ||
| ); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| nav.gnb { | ||
| z-index: 20; | ||
| position: sticky; | ||
| top: 0; | ||
| left: 0; | ||
| right: 0; | ||
| height: 60px; | ||
| border-bottom: 1px solid var(--slate200); | ||
| background-color: white; | ||
| } | ||
|
|
||
| nav.gnb > div { | ||
| max-width: 1200px; | ||
| margin: 0 auto; | ||
| padding: 10px 24px 9px; | ||
| line-height: 0; | ||
| } | ||
|
|
||
| nav.gnb > div a { | ||
| display: inline-block; | ||
| line-height: 0; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| import Link from "next/link"; | ||
| import Image from "next/image"; | ||
| import logo from "@/assets/images/logo_text.png"; | ||
| import styles from "./Gnb.module.css"; | ||
|
|
||
| export default function Gnb() { | ||
| return ( | ||
| <nav className={styles.gnb}> | ||
| <div> | ||
| <Link href="/"> | ||
| <Image height={40} alt="logo" src={logo} /> | ||
| </Link> | ||
| </div> | ||
| </nav> | ||
| ); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| .search { | ||
| position: relative; | ||
| display: block; | ||
| height: 56px; | ||
| } | ||
|
|
||
| .search > * { | ||
| width: calc(100% - 4px); | ||
| height: 52.5px; | ||
| border: 2px solid var(--slate900); | ||
| border-radius: 24px; | ||
| } | ||
|
|
||
| .search > input { | ||
| position: absolute; | ||
| top: 0; | ||
| left: 0; | ||
| padding: 15px 22px; | ||
| background-color: var(--slate100); | ||
| color: var(--slate900); | ||
| line-height: 18px; | ||
| font-size: 16px; | ||
| font-weight: 400; | ||
| } | ||
|
|
||
| .search > input::placeholder { | ||
| color: var(--slate500); | ||
| } | ||
|
|
||
| .search > div { | ||
| position: absolute; | ||
| bottom: 0; | ||
| right: 0; | ||
| background-color: var(--slate900); | ||
| } |
GANGYIKIM marked this conversation as resolved.
Show resolved
Hide resolved
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| import { InputHTMLAttributes, Ref } from "react"; | ||
| import styles from "./Search.module.css"; | ||
|
|
||
| interface Props extends InputHTMLAttributes<HTMLInputElement> { | ||
| ref?: Ref<HTMLInputElement>; | ||
| } | ||
|
|
||
| export default function Search({ className = "", style, ...props }: Props) { | ||
| return ( | ||
| <label className={`${styles.search} ${className}`} style={style}> | ||
| <div /> | ||
| <input placeholder="할 일을 입력해주세요" {...props} /> | ||
| </label> | ||
| ); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,62 @@ | ||
| import axios from "axios"; | ||
|
|
||
| const api = axios.create({ | ||
| baseURL: process.env.NEXT_PUBLIC_BASE_URL, | ||
| }); | ||
|
|
||
| export type SimpleItem = { | ||
| isCompleted: boolean; | ||
| name: string; | ||
| id: number; | ||
| }; | ||
|
|
||
| export type ResponseSimpleItems = SimpleItem[]; | ||
|
|
||
| export type ResponseItem = { | ||
| isCompleted: boolean; | ||
| imageUrl: string | null; | ||
| memo: string | null; | ||
| name: string; | ||
| tenantId: string; | ||
| id: number; | ||
| }; | ||
Moon-ju-young marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| export type ResponseDelete = { | ||
| message: string; | ||
| }; | ||
|
|
||
| export type PostItem = { | ||
| name: string; | ||
| }; | ||
|
|
||
| export type PatchItem = { | ||
| name?: string; | ||
| memo?: string; | ||
| imageUrl?: string; | ||
| isCompleted?: boolean; | ||
| }; | ||
|
|
||
| export async function getItems() { | ||
| const response = await api.get<ResponseSimpleItems>("/items"); | ||
| return response.data; | ||
| } | ||
|
|
||
| export async function getItem(itemId: number) { | ||
| const response = await api.get<ResponseItem>("/items/" + itemId); | ||
| return response.data; | ||
| } | ||
|
|
||
| export async function postItem(body: PostItem) { | ||
| const response = await api.post<ResponseItem>("/items", body); | ||
| return response.data; | ||
| } | ||
|
|
||
| export async function patchItem(itemId: number, body: PatchItem) { | ||
| const response = await api.patch<ResponseItem>("/items/" + itemId, body); | ||
| return response.data; | ||
| } | ||
|
|
||
| export async function deleteItem(itemId: number) { | ||
| const response = await api.delete<ResponseDelete>("/items/" + itemId); | ||
| return response.data; | ||
| } | ||
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.
💊 제안
체크박스의 경우 해당 이미지 없이도 구현이 가능하니, html과 css만으로 구현하시는 것이 성능이나 유지보수 측면에서 더 좋을 것 같아요!