-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* memo * fix * feat * fix * fix * f * feat * feat: post * put * fix * f
- Loading branch information
1 parent
e69a4a7
commit 844a7b2
Showing
13 changed files
with
397 additions
and
2 deletions.
There are no files selected for viewing
Binary file not shown.
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,13 @@ | ||
import { http } from '@/apis/http'; | ||
import { useMutation } from '@tanstack/react-query'; | ||
|
||
export const deleteMemo = (cardId: string, memoId: number) => | ||
http.delete({ | ||
url: `/cards/${cardId}/card-memo/${memoId}`, | ||
}); | ||
|
||
export const useDeleteMemo = (cardId: string) => | ||
useMutation({ | ||
mutationKey: ['delete-memo', cardId], | ||
mutationFn: (memoId: number) => deleteMemo(cardId, memoId), | ||
}); |
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,20 @@ | ||
import { http } from '@/apis/http'; | ||
import { useSuspenseQuery } from '@tanstack/react-query'; | ||
|
||
export type GetMemosResponse = Array<{ | ||
id: number; | ||
content: string; | ||
updatedAt: string; | ||
}>; | ||
|
||
const getMemos = (cardId: string) => | ||
http.get<GetMemosResponse>({ | ||
url: `/cards/${cardId}/card-memo`, | ||
}); | ||
|
||
export const useGetMemos = (cardId: string) => | ||
useSuspenseQuery({ | ||
queryKey: ['get-memos', cardId], | ||
queryFn: () => getMemos(cardId), | ||
select: ({ result }) => result, | ||
}); |
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,14 @@ | ||
import { http } from '@/apis/http'; | ||
import { useMutation } from '@tanstack/react-query'; | ||
|
||
export const postMemo = (cardId: string, content: string) => | ||
http.post({ | ||
url: `/cards/${cardId}/card-memo`, | ||
data: { content }, | ||
}); | ||
|
||
export const usePostMemo = (cardId: string) => | ||
useMutation({ | ||
mutationKey: ['post-memo', cardId], | ||
mutationFn: (content: string) => postMemo(cardId, content), | ||
}); |
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,14 @@ | ||
import { http } from '@/apis/http'; | ||
import { useMutation } from '@tanstack/react-query'; | ||
|
||
export const putMemo = (cardId: string, memoId: number, content: string) => | ||
http.put({ | ||
url: `/cards/${cardId}/card-memo/${memoId}/content`, | ||
data: { content }, | ||
}); | ||
|
||
export const usePutMemo = (cardId: string) => | ||
useMutation({ | ||
mutationKey: ['put-memo', cardId], | ||
mutationFn: ({ memoId, content }: { memoId: number; content: string }) => putMemo(cardId, memoId, content), | ||
}); |
75 changes: 75 additions & 0 deletions
75
src/app/(sidebar)/write/[id]/components/MemoContainer/Memo/Memo.tsx
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,75 @@ | ||
'use client'; | ||
|
||
import { useState, useRef, useEffect, useCallback } from 'react'; | ||
import { Textarea } from '@/system/components/Textarea/Textarea'; | ||
import { RemoveMemo } from '@/system/components/Icon/SVG/RemoveMemo'; | ||
import { AnimatePresence } from 'framer-motion'; | ||
import { motion } from 'framer-motion'; | ||
import { GetMemosResponse } from '../../../api/useGetMemos'; | ||
import { useMemosContext } from '../../../fetcher/MemosFetcher'; | ||
import { useDeleteMemo } from '../../../api/useDeleteMemo'; | ||
import { usePutMemo } from '@/app/(sidebar)/write/[id]/api/usesPutMemo'; | ||
|
||
export default function Memo({ id: memoId, content, updatedAt }: GetMemosResponse[number]) { | ||
const { cardId } = useMemosContext(); | ||
const prevMemo = useRef<string>(content); | ||
const [memo, setMemo] = useState(content || ''); | ||
const [showCloseButton, setShowCloseButton] = useState(false); | ||
const textareaRef = useRef<HTMLTextAreaElement>(null); | ||
const { mutate } = useDeleteMemo(cardId); | ||
const { mutate: putMemo } = usePutMemo(cardId); | ||
|
||
const adjustTextareaHeight = useCallback(() => { | ||
const textarea = textareaRef.current; | ||
|
||
if (textarea) { | ||
textarea.style.height = 'auto'; | ||
textarea.style.height = `${textarea.scrollHeight}px`; | ||
} | ||
}, []); | ||
|
||
useEffect(() => { | ||
adjustTextareaHeight(); | ||
|
||
if (prevMemo.current !== memo) { | ||
putMemo({ memoId, content: memo }); | ||
} | ||
}, [memo]); | ||
|
||
return ( | ||
<div | ||
// using pure css in memo.css | ||
className="memo-wrap" | ||
onMouseEnter={() => setShowCloseButton(true)} | ||
onMouseLeave={() => setShowCloseButton(false)}> | ||
<div className="absolute bottom-27 w-360 z-[100] h-2 bg-white" /> | ||
|
||
<div className="w-360 h-auto bg-white rounded-tl-8 rounded-tr-8 px-16 pt-16 pb-8"> | ||
<Textarea | ||
ref={textareaRef} | ||
rows={1} | ||
className="w-full min-h-0 border-none p-0 memo-14 resize-none focus:outline-0 focus-visible:ring-0 focus-visible:ring-offset-0 rounded-0" | ||
value={memo} | ||
onChange={(e) => setMemo(e.target.value)} | ||
autoFocus | ||
/> | ||
</div> | ||
|
||
<AnimatePresence mode="wait"> | ||
{showCloseButton && ( | ||
<motion.button | ||
initial={{ opacity: 0 }} | ||
animate={{ opacity: 1 }} | ||
transition={{ duration: 0.2 }} | ||
exit={{ opacity: 0 }} | ||
className="absolute top-16 right-32" | ||
onClick={() => mutate(memoId)}> | ||
<RemoveMemo size={24} color="#37383C" /> | ||
</motion.button> | ||
)} | ||
</AnimatePresence> | ||
|
||
<div className="memo pl-16 memo-10 pb-16 memo-neutral-35">{updatedAt.split(' ')[0].replaceAll('-', '.')}</div> | ||
</div> | ||
); | ||
} |
57 changes: 57 additions & 0 deletions
57
src/app/(sidebar)/write/[id]/components/MemoContainer/MemoContainer.tsx
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,57 @@ | ||
import { Button, Icon } from '@/system/components'; | ||
import { Textarea } from '@/system/components/Textarea/Textarea'; | ||
import { useState } from 'react'; | ||
import Memo from './Memo/Memo'; | ||
import { cn } from '@/utils'; | ||
import { useMemosContext } from '../../fetcher/MemosFetcher'; | ||
import { usePostMemo } from '@/app/(sidebar)/write/[id]/api/usePostMemo'; | ||
|
||
const TEXT_DEFAULT_HEIGHT = 22; | ||
const TEXT_FOCUS_HEIGHT = 80; | ||
|
||
export default function MemoContainer() { | ||
const { memos, cardId } = useMemosContext(); | ||
const [memo, setMemo] = useState<string>(''); | ||
const [textareaHeight, setTextareaHeight] = useState(TEXT_DEFAULT_HEIGHT); | ||
const { mutate } = usePostMemo(cardId); | ||
|
||
return ( | ||
<section className="min-w-400 h-screen border-1 bg-neutral-1"> | ||
<div className="flex items-end p-16 w-full h-109 gap-8"> | ||
<Icon name="filledMemo" size={24} /> | ||
<p className="text-18 font-semibold">메모</p> | ||
</div> | ||
|
||
<div className="w-full h-[calc(100vh-294px)] px-16 flex flex-col gap-16 overflow-y-scroll"> | ||
{memos.map((memo) => ( | ||
<Memo key={memo.id} {...memo} /> | ||
))} | ||
</div> | ||
|
||
<div className="max-w-400 relative px-16 pt-16 pb-24 h-185 flex flex-col justify-end"> | ||
<div className="pt-13 px-16 pb-8 rounded-8 border-1 border-neutral-20 bg-white flex flex-col gap-4"> | ||
<Textarea | ||
value={memo} | ||
onChange={(e) => setMemo(e.target.value)} | ||
onFocus={() => setTextareaHeight(TEXT_FOCUS_HEIGHT)} | ||
onBlur={() => setTextareaHeight(TEXT_DEFAULT_HEIGHT)} | ||
rows={1} | ||
className={cn( | ||
'resize-none min-h-0 bg-white border-none focus:outline-0 focus-visible:ring-0 focus-visible:ring-offset-0', | ||
textareaHeight === TEXT_DEFAULT_HEIGHT && 'overflow-hidden', | ||
)} | ||
style={{ height: `${textareaHeight}px`, transition: 'height 0.2s ease-in-out' }} | ||
maxLength={130} | ||
/> | ||
|
||
<div className="flex justify-between items-center w-full h-32"> | ||
<p className="text-10 text-neutral-60">{memo.length} / 130</p> | ||
<Button onClick={() => mutate(memo)}> | ||
<Icon name="submitArrow" size={32} color="#1B1C1E" /> | ||
</Button> | ||
</div> | ||
</div> | ||
</div> | ||
</section> | ||
); | ||
} |
File renamed without changes.
File renamed without changes.
File renamed without changes.
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,19 @@ | ||
import { generateContext } from '@/lib'; | ||
import { GetMemosResponse, useGetMemos } from '../api/useGetMemos'; | ||
import { StrictPropsWithChildren } from '@/types'; | ||
|
||
const [MemosProvider, useMemosContext] = generateContext<{ memos: GetMemosResponse; cardId: string }>({ | ||
name: 'memos-provider', | ||
}); | ||
|
||
function MemosFetcher({ cardId, children }: StrictPropsWithChildren<{ cardId: string }>) { | ||
const { data } = useGetMemos(cardId); | ||
|
||
return ( | ||
<MemosProvider memos={data} cardId={cardId}> | ||
{children} | ||
</MemosProvider> | ||
); | ||
} | ||
|
||
export { MemosFetcher, useMemosContext }; |
Oops, something went wrong.