From 696792af26c69cca347ce1b4a554773da7f90bc3 Mon Sep 17 00:00:00 2001 From: typhoon0678 Date: Sat, 15 Mar 2025 13:17:46 +0900 Subject: [PATCH 1/4] =?UTF-8?q?design:=20=ED=9A=8C=EC=9B=90=EA=B0=80?= =?UTF-8?q?=EC=9E=85,=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20input=20focus=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/member/LoginTextField.tsx | 8 +++++-- src/pages/member/EmailPage.tsx | 9 ++++++- src/pages/member/LoginPage.tsx | 30 +++++++++++++++++++----- src/pages/member/SignupPage.tsx | 28 ++++++++++++++++++---- 4 files changed, 61 insertions(+), 14 deletions(-) diff --git a/src/components/member/LoginTextField.tsx b/src/components/member/LoginTextField.tsx index 7ac5131..f3dc216 100644 --- a/src/components/member/LoginTextField.tsx +++ b/src/components/member/LoginTextField.tsx @@ -1,11 +1,13 @@ import * as React from "react"; +import {Ref} from "react"; -function LoginTextField({label, type, placeholder, value, setValue, error, isError, onKeyDown}: { +function LoginTextField({label, type, placeholder, value, setValue, customRef, error, isError, onKeyDown}: { label?: string, type: string, placeholder: string, value: string, setValue: (value: string) => void, + customRef?: Ref, error?: string, isError?: boolean, onKeyDown?: (event: React.KeyboardEvent) => void, @@ -20,7 +22,9 @@ function LoginTextField({label, type, placeholder, value, setValue, error, isErr className="bg-gray-50 border border-gray-300 text-gray-900 text-md rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5" value={value} onChange={(event => setValue(event.target.value))} - onKeyDown={onKeyDown}/> + onKeyDown={onKeyDown} + ref={customRef} + autoComplete={"on"}/> {(error) && ( isError ?

{error}

diff --git a/src/pages/member/EmailPage.tsx b/src/pages/member/EmailPage.tsx index 3eea84b..1398c4d 100644 --- a/src/pages/member/EmailPage.tsx +++ b/src/pages/member/EmailPage.tsx @@ -1,6 +1,6 @@ import BasicLayout from "../../layout/BasicLayout.tsx"; import LoginTextField from "../../components/member/LoginTextField.tsx"; -import {useState} from "react"; +import {useEffect, useRef, useState} from "react"; import LoginButton from "../../components/member/LoginButton.tsx"; import {useNavigate} from "react-router-dom"; import * as React from "react"; @@ -15,6 +15,8 @@ function EmailPage() { const navigate = useNavigate(); + const emailRef = useRef(null); + const handleEmailEnter = (event: React.KeyboardEvent) => { if (event.key !== "Enter") { return; @@ -38,6 +40,10 @@ function EmailPage() { .finally(() => setLoading(false)); } + useEffect(() => { + emailRef.current?.focus(); + }, []); + return (
setEmail(value)} + customRef={emailRef} onKeyDown={handleEmailEnter}/>
diff --git a/src/pages/member/LoginPage.tsx b/src/pages/member/LoginPage.tsx index 4e4c488..af093d8 100644 --- a/src/pages/member/LoginPage.tsx +++ b/src/pages/member/LoginPage.tsx @@ -1,6 +1,6 @@ import BasicLayout from "../../layout/BasicLayout.tsx"; import {useNavigate} from "react-router-dom"; -import {useState} from "react"; +import {useEffect, useRef, useState} from "react"; import {useDispatch, useSelector} from "react-redux"; import {getProfile, handleKakaoLogin, loginApi} from "../../common/apis/member.tsx"; import {login, setProfile} from "../../common/slices/loginSlice.tsx"; @@ -18,6 +18,9 @@ function LoginPage() { const navigate = useNavigate(); const dispatch = useDispatch(); + const emailRef = useRef(null); + const passwordRef = useRef(null); + const [loginInfo, setLoginInfo] = useState({email: "", password: ""}); const handleLogin = () => { @@ -45,6 +48,14 @@ function LoginPage() { .catch(error => alert(error.response.data.message)); } + const handleEmailEnter = (event: React.KeyboardEvent) => { + if (event.key !== "Enter") { + return; + } + + passwordRef.current?.focus(); + } + const handlePasswordEnter = (event: React.KeyboardEvent) => { if (event.key !== "Enter") { return; @@ -57,6 +68,10 @@ function LoginPage() { handleLogin(); } + useEffect(() => { + emailRef.current?.focus(); + }, []); + return (
-
+
e.preventDefault()} className="flex flex-col justify-center items-center w-full"> setLoginInfo({...loginInfo, email: value})}/> + setValue={(value) => setLoginInfo({...loginInfo, email: value})} + customRef={emailRef} + onKeyDown={handleEmailEnter}/> setLoginInfo({...loginInfo, password: value})} + customRef={passwordRef} onKeyDown={handlePasswordEnter}/> -
+
(null); + const confirmPasswordRef = useRef(null); + const handlePasswordEnter = (event: React.KeyboardEvent) => { if (event.key !== "Enter") { return; } + confirmPasswordRef.current?.focus(); + } + + const handleConfirmPasswordEnter = (event: React.KeyboardEvent) => { + if (event.key !== "Enter") { + return; + } + handleSignup(); } @@ -51,26 +62,33 @@ function SignupPage() { return !checkPassword(passwordInfo.password) || (passwordInfo.password !== passwordInfo.confirmPassword); } + useEffect(() => { + passwordRef.current?.focus(); + }, []); + return (

비밀번호 설정을 마치면 회원가입이 완료됩니다.

-
+
e.preventDefault()} className="w-full"> setPasswordInfo({...passwordInfo, password: value})}/> + setValue={(value) => setPasswordInfo({...passwordInfo, password: value})} + customRef={passwordRef} + onKeyDown={handlePasswordEnter}/> setPasswordInfo({...passwordInfo, confirmPassword: value})} - onKeyDown={handlePasswordEnter}/> -
+ customRef={confirmPasswordRef} + onKeyDown={handleConfirmPasswordEnter}/> +
From 3b11d4be8ec1e6548e52a90bf5fc5109a902fcdb Mon Sep 17 00:00:00 2001 From: typhoon0678 Date: Sat, 15 Mar 2025 13:33:59 +0900 Subject: [PATCH 2/4] =?UTF-8?q?design:=20=ED=8F=B4=EB=8D=94=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20input=20focus,=20enter=20=EC=9E=85=EB=A0=A5=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/common/Header.tsx | 2 +- src/components/folder/FolderCardList.tsx | 26 +++++++++++++++++++++++- src/pages/setting/FolderSettingPage.tsx | 5 +++++ 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/src/components/common/Header.tsx b/src/components/common/Header.tsx index c34bd76..7ddfcd2 100644 --- a/src/components/common/Header.tsx +++ b/src/components/common/Header.tsx @@ -123,7 +123,7 @@ function Header() {
:
- +
} diff --git a/src/components/folder/FolderCardList.tsx b/src/components/folder/FolderCardList.tsx index a801cf3..7f74d79 100644 --- a/src/components/folder/FolderCardList.tsx +++ b/src/components/folder/FolderCardList.tsx @@ -6,6 +6,8 @@ import {FillButton} from "../common/FillButton.tsx"; import {MdOutlineMenu} from "react-icons/md"; import {TextButton} from "../common/TextButton.tsx"; import {restrictToVerticalAxis} from "@dnd-kit/modifiers"; +import {useEffect, useRef} from "react"; +import * as React from "react"; function FolderCardList({ folders, @@ -87,6 +89,8 @@ function FolderCard({ setSelectedFolder: (selectedFolder: FolderType) => void, }) { + const folderTitleRef = useRef(null); + const {attributes, listeners, setNodeRef, transform, transition} = useSortable({id: folder.id}); const style = { @@ -94,6 +98,24 @@ function FolderCard({ transition, }; + useEffect(() => { + if (folder.id === editFolderId) { + folderTitleRef.current?.focus(); + } + }, [editFolderId]); + + const handleEditTitleEnter = (event: React.KeyboardEvent) => { + if (event.key !== "Enter") { + return; + } + + handleEditTitle(); + } + + const handleEditTitle = () => { + handleEdit({...folder, title: editFolderTitle}); + } + return (
@@ -105,13 +127,15 @@ function FolderCard({ className={"flex-1 font-bold border p-2"} value={editFolderTitle} onChange={(e) => setEditFolderTitle(e.target.value)} + onKeyDown={handleEditTitleEnter} + ref={folderTitleRef} placeholder="폴더 이름"/>
setEditFolderId("")} addStyle={"!bg-gray-400 hover:brightness-110"}/> handleEdit({...folder, title: editFolderTitle})} + onClick={handleEditTitle} addStyle={`${folder.title === editFolderTitle && "opacity-50 hover:!cursor-auto"}`} disabled={folder.title === editFolderTitle}/>
diff --git a/src/pages/setting/FolderSettingPage.tsx b/src/pages/setting/FolderSettingPage.tsx index c5d376a..e6a17e5 100644 --- a/src/pages/setting/FolderSettingPage.tsx +++ b/src/pages/setting/FolderSettingPage.tsx @@ -269,6 +269,11 @@ function FolderSettingPage() { } const handleSubmit = () => { + if (editFolderId !== "") { + alert("변경중인 폴더가 있습니다."); + return; + } + if (!confirm("변경사항을 저장하시겠습니까?")) { return; } From f25faf2bb42d2543e7bee91bbe75c3cb0d14988b Mon Sep 17 00:00:00 2001 From: typhoon0678 Date: Sat, 15 Mar 2025 14:34:07 +0900 Subject: [PATCH 3/4] =?UTF-8?q?style:=20tag=20Enter,=20outFocus=20?= =?UTF-8?q?=EC=9E=85=EB=A0=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/blog/BlogSideBar.tsx | 14 +++++-- src/components/post/PostCard.tsx | 13 +++---- src/pages/MainPage.tsx | 2 +- src/pages/post/PostPage.tsx | 8 ++-- src/pages/post/SearchPage.tsx | 28 ++++++++------ src/pages/post/WritePage.tsx | 56 +++++++++++++++++++-------- src/pages/setting/PostSettingPage.tsx | 4 +- 7 files changed, 80 insertions(+), 45 deletions(-) diff --git a/src/components/blog/BlogSideBar.tsx b/src/components/blog/BlogSideBar.tsx index 4aebc2a..be55dd6 100644 --- a/src/components/blog/BlogSideBar.tsx +++ b/src/components/blog/BlogSideBar.tsx @@ -44,13 +44,17 @@ function BlogSideBar({folders, tags, username, profileUrl, selectedFolder, setSe
태그
-
+
{tags.map((tag) => ( ))} + {tags.length === 0 && +
+ 생성된 태그가 없습니다. +
}
@@ -78,7 +82,7 @@ function BlogFolderList({depth = 0, folders, selectedFolder, setSelectedFolder}: return (
- {folders.map(folder => + {folders.length > 1 && folders.map(folder =>
{depth === 0 &&
} - {depth === 0 &&
} + {folder.subFolders.length > 0 && depth === 0 &&
}
)} + {depth === 0 && folders.length <= 1 && +
+ 생성된 폴더가 없습니다. +
}
); } diff --git a/src/components/post/PostCard.tsx b/src/components/post/PostCard.tsx index b2ddedd..e256304 100644 --- a/src/components/post/PostCard.tsx +++ b/src/components/post/PostCard.tsx @@ -4,7 +4,6 @@ import DOMPurify from "dompurify"; import {MdImage} from "react-icons/md"; import {dateToKorean} from "../../common/util/date.tsx"; import {Link} from "react-router-dom"; -import {FaRegHeart} from "react-icons/fa6"; function PostCard({post}: { post: PostResponse }) { @@ -33,12 +32,12 @@ function PostCard({post}: { post: PostResponse }) { {dateToKorean(post.createdAt)}
-
-
- -

20

-
-
+ {/*
*/} + {/*
*/} + {/* */} + {/*

20

*/} + {/*
*/} + {/*
*/}
diff --git a/src/pages/MainPage.tsx b/src/pages/MainPage.tsx index 48774ec..1285c54 100644 --- a/src/pages/MainPage.tsx +++ b/src/pages/MainPage.tsx @@ -34,7 +34,7 @@ function MainPage() {
- 인기 있는 게시글 + 최근 게시글
diff --git a/src/pages/post/PostPage.tsx b/src/pages/post/PostPage.tsx index db145ce..0153037 100644 --- a/src/pages/post/PostPage.tsx +++ b/src/pages/post/PostPage.tsx @@ -180,7 +180,7 @@ function PostPage() {
-
+
Home @@ -192,7 +192,7 @@ function PostPage() {
}
-
+
{post.username} @@ -211,9 +211,9 @@ function PostPage() { }}/>)}
-
-
+
{loginState.isLogin ? (null); const orderRef = useRef(null); + const searchRef = useRef(null); const [openOption, setOpenOption] = useState(false); const [openOrder, setOpenOrder] = useState(false); @@ -167,6 +168,10 @@ function SearchPage() { .catch(error => alert(error.response.data.message)); } + useEffect(() => { + searchRef.current?.focus(); + }, []); + useEffect(() => { if (refresh) { setRefresh(false); @@ -201,6 +206,7 @@ function SearchPage() { onChange={(e) => setSearchRequest({...searchRequest, keyword: e.target.value})} placeholder={"검색어를 입력해주세요."} className="w-full block mt-0.5 p-3 mr-4 font-jalnan text-xl text-gray-900 border-b-2 border-white focus:outline-none focus:border-black" + ref={searchRef} onKeyDown={handleSearchEnter}/>
-
-
+
+
+ {selectedTab === "post" ? postPageInfo.totalElements : blogPageInfo.totalElements} - 개의 검색결과 + + 개의 검색결과
{selectedTab === "post" && -
+
void, value: string, @@ -354,11 +359,10 @@ function SearchMenu({title, menus, setMenu, value, open, handleOpen, customRef}: return (
-
-

{title}

+
)} handleTag(event.target.value)} + onChange={(event) => handleInputTag(event.target.value)} onFocus={() => setShowTag(true)} - onBlur={() => setShowTag(false)}/> + onBlur={() => { + setShowTag(false); + handleTag(post.inputTag); + }} + onKeyDown={handleTagEnter}/>
{showTag &&
- 쉼표를 입력하여 태그를 등록할 수 있습니다. + className="absolute -bottom-6 left-0 flex flex-col rounded-md bg-gray-700 text-white w-[calc(346px)] px-4 py-2 text-sm"> + 쉼표나 엔터를 입력하여 태그를 등록할 수 있습니다.
}
- +
} {!isFolderEdit &&
- +
}
From e9ef43d24f75f126594c46227733580b6461026e Mon Sep 17 00:00:00 2001 From: typhoon0678 Date: Sat, 15 Mar 2025 15:03:35 +0900 Subject: [PATCH 4/4] =?UTF-8?q?fix:=20writepage=20=EB=92=A4=EB=A1=9C?= =?UTF-8?q?=EA=B0=80=EA=B8=B0=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/folder/FolderSelectBox.tsx | 2 +- src/pages/post/WritePage.tsx | 13 ++----------- 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/src/components/folder/FolderSelectBox.tsx b/src/components/folder/FolderSelectBox.tsx index 451dc1f..cdad268 100644 --- a/src/components/folder/FolderSelectBox.tsx +++ b/src/components/folder/FolderSelectBox.tsx @@ -74,7 +74,7 @@ function FolderSelectCard({folder, depth, selectedFolder, setTargetFolder, setFo
{getFolderTitle(folder.title, depth)} -

({folder.postCount})

+ {folder.id !== "empty" &&

({folder.postCount})

}
{folder.subFolders && diff --git a/src/pages/post/WritePage.tsx b/src/pages/post/WritePage.tsx index 413047f..9834f1f 100644 --- a/src/pages/post/WritePage.tsx +++ b/src/pages/post/WritePage.tsx @@ -1,6 +1,6 @@ import BasicLayout from "../../layout/BasicLayout.tsx"; import {useEffect, useState} from "react"; -import {useBlocker, useLocation, useNavigate, useParams} from "react-router-dom"; +import {useLocation, useNavigate, useParams} from "react-router-dom"; import {Editor} from "@tinymce/tinymce-react"; import {FillButton} from "../../components/common/FillButton.tsx"; import {useSelector} from "react-redux"; @@ -44,7 +44,6 @@ function WritePage() { const [targetFolder, setTargetFolder] = useState(null); const [uploadCount, setUploadCount] = useState(0); - const [exitPage, setExitPage] = useState(false); const removeTag = (tag: string | null) => { setPost({...post, tags: post.tags.filter(prevTag => prevTag !== tag)}); @@ -107,7 +106,6 @@ function WritePage() { }) .then(() => { alert("작성되었습니다."); - setExitPage(true); navigate(`/blog/${loginState.username}`); }) .catch((error) => alert(error.response.data.message)) @@ -139,7 +137,6 @@ function WritePage() { }) .then(() => { alert("수정되었습니다."); - setExitPage(true); navigate(`/blog/${loginState.username}`); }) .catch((error) => alert(error.response.data.message)) @@ -159,13 +156,6 @@ function WritePage() { .catch((error) => alert(error.response.data.message)); } - // 뒤로가기 방지 - useBlocker(() => { - return ((post.title !== "" || post.content !== "") && - !exitPage && !confirm("페이지를 이동하시겠습니까?\n\n작성중인 내용이 저장되지 않습니다.")); - } - ); - useEffect(() => { // 새로고침 방지 const handleBeforeUnload = (event: BeforeUnloadEvent) => { @@ -191,6 +181,7 @@ function WritePage() { getMemberFolders(loginState.username) .then(res => { + console.log(res); setFolders(toFolderTypeList(res.data)); setFolders(prev => [ {