Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/common/router/root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const root = createBrowserRouter([
element: <Suspense fallback={Loading}><Login/></Suspense>
},
{
path: '/setting',
path: '/setting/:section',
element: <Suspense fallback={Loading}><Setting/></Suspense>
},
{
Expand Down
2 changes: 1 addition & 1 deletion src/common/util/html.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,6 @@ export const getImgUrls = (content: string) => {
}

export const removeHtmlTags = (content: string) => {
const regex = /(<([^>]+)>)/gi;
const regex = /(<([^>]+)>|&nbsp;)/gi;
return content.replace(regex, "");
}
9 changes: 9 additions & 0 deletions src/common/util/string.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export const getFolderTitle = (title: string, depth: number) => {
let prefix = "";
if (depth === 1) {
prefix = "↳";
} else if (depth === 2) {
prefix = "-";
}
return `${prefix} ${title}`;
}
17 changes: 17 additions & 0 deletions src/components/blog/BlogSearchBar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
function BlogSearchBar({value, setValue}: {
value: string,
setValue: (value: string) => void,
}) {
return (
<input
className="border border-gray-400 p-2 text-sm"
type="text"
value={value}
onChange={(e) => {
setValue(e.target.value)
}}>
</input>
);
}

export default BlogSearchBar;
88 changes: 88 additions & 0 deletions src/components/blog/BlogSideBar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import {FolderType} from "../../common/types/blog.tsx";
import {useSelector} from "react-redux";
import {RootState} from "../../store.tsx";
import {faker} from "@faker-js/faker/locale/ko";
import {OutlineLink} from "../common/OutlineButton.tsx";
import BlogTagCard from "./BlogTagCard.tsx";
import {getFolderTitle} from "../../common/util/string.tsx";

function BlogSideBar({folders, username, addTag, setSelectedFolder, bgColor, side}: {
folders: FolderType[],
username: string | undefined,
addTag: (tagName: string) => void,
setSelectedFolder: (folder: FolderType) => void,
bgColor?: string,
side?: boolean,
}) {

const loginState = useSelector((state: RootState) => state.loginSlice);

return (
<div className={`${bgColor} ${side && "h-screen overflow-y-scroll"}`}>
<div className="flex flex-col justify-start items-center py-4 gap-4 z-200">
<img className="border border-gray-300 h-32 w-32 rounded-full"
src={faker.image.avatar()} alt="username"/>
<div className="flex justify-center items-center text-2xl font-black">
{username}
</div>
{username === loginState.username && (
<div className="flex justify-between items-center gap-x-4 text-xs">
<OutlineLink text={"게시글 작성"} to={"/write"}/>
<OutlineLink text={"블로그 설정"} to={"/setting/folder"}
addStyle={"!border-neutral-500 !text-neutral-500 hover:bg-neutral-500 hover:!text-white"}/>
</div>
)}
</div>
<div className="w-72 mx-auto flex flex-col justify-center items-start gap-4 py-8">
<div className="w-full font-bold text-lime-700 text-center">폴더</div>
<BlogFolderList
folders={folders}
setSelectedFolder={setSelectedFolder}/>
</div>
<div className="flex flex-col justify-center items-center gap-4 py-8">
<div className="font-bold text-lime-700 text-center">태그</div>
<div className="w-72 flex flex-wrap justify-center gap-x-2 gap-y-4 p-2">
{[Array.from({length: 24}).map(() => (
<BlogTagCard key={faker.number.int().toString()}
tag={{
id: faker.number.int().toString(),
name: faker.word.sample()
}}
addTag={addTag}/>
))]}
</div>
</div>
</div>
);
}

function BlogFolderList({depth = 0, folders, setSelectedFolder}: {
depth?: number,
folders: FolderType[],
setSelectedFolder: (folder: FolderType) => void,
}) {

return (
<div className="w-full flex flex-col gap-y-1">
{folders.map(folder =>
<div key={folder.id} className={`flex flex-col gap-y-2`}>
{depth === 0 && <div className="h-2"/>}
<button
className={`flex justify-start items-center hover:opacity-50 hover:cursor-pointer
${depth === 0 && "font-bold"}
${depth > 0 && `text-gray-500`}`}
onClick={() => setSelectedFolder(folder)}>
{depth === 2 && <div className="w-8"/>}
{getFolderTitle(folder.title, depth)}
</button>
{depth === 0 && <hr className="text-gray-400"/>}
<BlogFolderList
depth={depth + 1}
folders={folder.subFolders}
setSelectedFolder={setSelectedFolder}/>
</div>)}
</div>
);
}

export default BlogSideBar;
16 changes: 16 additions & 0 deletions src/components/blog/BlogTagCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import {TagResponse} from "../../common/types/post.tsx";
import {TextButton} from "../common/TextButton.tsx";

function BlogTagCard({tag, addTag}: {
tag: TagResponse,
addTag: (tagName: string) => void,
}) {
return (
<TextButton
text={tag.name}
onClick={() => addTag(tag.name)}
addStyle={"!px-1 !py-0 hover:text-lime-700"}/>
);
}

export default BlogTagCard;
28 changes: 22 additions & 6 deletions src/components/common/FillButton.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,41 @@
import {Link} from "react-router-dom";

const className = " bg-lime-500 hover:bg-lime-400 text-white font-bold py-2 px-4 rounded hover:cursor-pointer";
const className = " bg-lime-500 hover:brightness-105 text-white text-sm font-semibold py-2 px-4 rounded hover:cursor-pointer";

export function FillButton({text, onClick, addStyle}: { text: string, onClick: () => void, addStyle?: string }) {
export function FillButton({text, onClick, addStyle, disabled}: {
text: string,
onClick: () => void,
addStyle?: string,
disabled?: boolean
}) {
return (
<button onClick={onClick} className={(addStyle) ? addStyle + className : className}>
<button
onClick={onClick}
className={`${addStyle} ${className}`}
disabled={disabled}>
{text}
</button>
);
}

export function FillLink({text, to, addStyle}: { text: string, to: string, addStyle?: string }) {
return (
<Link to={to} className={(addStyle) ? addStyle + className : className}>
<Link to={to} className={`${addStyle} ${className}`}>
{text}
</Link>
);
}

export function LoadMoreButton({onClick, addStyle}: { onClick: () => void, addStyle?: string }) {
export function LoadMoreButton({onClick, addStyle, disabled}: {
onClick: () => void,
addStyle?: string,
disabled?: boolean
}) {
return (
<FillButton text={"더 불러오기"} onClick={onClick} addStyle={addStyle + " !bg-gray-400 hover:brightness-105"}/>
<FillButton
text={"더 불러오기"}
onClick={onClick}
addStyle={`${addStyle} "!bg-gray-400 hover:brightness-105"`}
disabled={disabled}/>
);
}
4 changes: 2 additions & 2 deletions src/components/common/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -98,9 +98,9 @@ function Header() {
className="w-full block px-4 py-2 text-gray-700 hover:bg-gray-100">
내 블로그
</Link>
<Link to={`/setting`}
<Link to={`/setting/profile`}
className="w-full block px-4 py-2 text-gray-700 hover:bg-gray-100">
설정
프로필 설정
</Link>
</div>
<div className="py-1">
Expand Down
4 changes: 2 additions & 2 deletions src/components/common/IconButton.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import {ReactNode} from 'react';

function IconButton({icon, onClick}: { icon: ReactNode; onClick: () => void }) {
function IconButton({icon, onClick, addStyle}: { icon: ReactNode; onClick: () => void, addStyle?: string }) {
return (
<button onClick={onClick} className="size-10 rounded-full hover:bg-gray-100 hover:cursor-pointer flex justify-center items-center">
<button onClick={onClick} className={`${addStyle} size-10 rounded-full hover:bg-gray-100 hover:cursor-pointer flex justify-center items-center`}>
{icon}
</button>
);
Expand Down
4 changes: 2 additions & 2 deletions src/components/common/OutlineButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@ const className = " bg-transparent hover:bg-lime-300 text-lime-700 font-semibold

export function OutlineButton({text, onClick, addStyle}: { text: string, onClick?: () => void, addStyle?: string }) {
return (
<button onClick={onClick} className={(addStyle) ? addStyle + className : className}>
<button onClick={onClick} className={`${addStyle} ${className}`}>
{text}
</button>
);
}

export function OutlineLink({text, to, addStyle}: { text: string, to: string, addStyle?: string }) {
return (
<Link to={to} className={(addStyle) ? addStyle + className : className}>
<Link to={to} className={`${addStyle} ${className}`}>
{text}
</Link>
);
Expand Down
101 changes: 73 additions & 28 deletions src/components/common/PaginationButton.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import {PageResponse} from "../../common/types/common.tsx";
import {MdArrowLeft, MdArrowRight} from "react-icons/md";
import {
MdKeyboardDoubleArrowLeft,
MdKeyboardDoubleArrowRight
} from "react-icons/md";
import {useEffect, useState} from "react";

function PaginationButton({pageInfo, setPage}: {
Expand All @@ -22,40 +25,82 @@ function PaginationButton({pageInfo, setPage}: {
} else {
setPageList(getArray(startPage, paginationSize));
}
}, [startPage]);
}, [startPage, pageInfo]);

return (
<div className="flex justify-center space-x-1">
<button
onClick={() => {
if (startPage !== 0) {
setStartPage(startPage - paginationSize);
}
}}
className="rounded-full py-2 px-3 text-center text-sm transition-all shadow-sm hover:shadow-lg text-slate-600 hover:text-white hover:bg-lime-400 hover:border-lime-400 hover:cursor-pointer ml-2">
<MdArrowLeft className="size-6"/>
<nav className="flex justify-center items-center gap-x-1" aria-label="Pagination">
<button type="button"
className="min-h-[38px] min-w-[38px] py-2 px-2.5 inline-flex jusify-center items-center gap-x-2 text-sm rounded-lg text-gray-800 hover:bg-gray-100 hover:cursor-pointer focus:outline-none focus:bg-gray-100 disabled:opacity-50 disabled:pointer-events-none"
hidden={!(startPage !== 0)}
onClick={() => {
if (startPage !== 0) {
setStartPage(startPage - paginationSize);
setPage(startPage - paginationSize);
}
}}
aria-label="Previous">
<MdKeyboardDoubleArrowLeft size={16}/>
<span className="sr-only">Previous</span>
</button>
{pageList.map((page) => (
<button
key={page}
<div className="flex items-center gap-x-1">
{pageList.map(currentPage =>
<button key={currentPage}
type="button"
className={`
${currentPage === pageInfo.number ? "bg-gray-200 focus:bg-gray-300 " : "hover:bg-gray-100 focus:bg-gray-100"}
min-h-[38px] min-w-[38px] flex justify-center items-center text-gray-800 py-2 px-3 text-sm rounded-lg hover:cursor-pointer focus:outline-none disabled:opacity-50 disabled:pointer-events-none`}
onClick={() => setPage(currentPage)}
aria-current="page">
{currentPage + 1}
</button>)}
</div>
<button type="button"
className="min-h-[38px] min-w-[38px] py-2 px-2.5 inline-flex jusify-center items-center gap-x-2 text-sm rounded-lg text-gray-800 hover:bg-gray-100 hover:cursor-pointer focus:outline-none focus:bg-gray-100 disabled:opacity-50 disabled:pointer-events-none"
hidden={!(pageInfo.totalPages > startPage + paginationSize)}
onClick={() => {
setPage(page)
if (pageInfo.totalPages > startPage + paginationSize) {
setStartPage(startPage + paginationSize);
setPage(startPage + paginationSize);
}
}}
className={`${pageInfo.number === page ? "bg-lime-400" : ""} min-w-9 rounded-full bg-white py-2 px-3.5 border border-transparent text-center text-sm transition-all shadow-md hover:shadow-lg focus:bg-lime-400 focus:shadow-none active:bg-lime-400 hover:bg-lime-400 active:shadow-none disabled:pointer-events-none disabled:opacity-50 disabled:shadow-none hover:cursor-pointer ml-2`}>
{page + 1}
</button>
))}
<button
onClick={() => {
if (pageInfo.totalPages > startPage + paginationSize) {
setStartPage(startPage + paginationSize);
}
}}
className="min-w-9 rounded-full py-2 px-3 text-center text-sm transition-all shadow-sm hover:shadow-lg text-slate-600 hover:text-white hover:bg-lime-400 hover:border-lime-400 hover:cursor-pointer ml-2">
<MdArrowRight className="size-6"/>
aria-label="Next">
<span className="sr-only">Next</span>
<MdKeyboardDoubleArrowRight size={16}/>
</button>
</div>
</nav>
// <div className="flex justify-center space-x-1">
// <button
// onClick={() => {
// if (startPage !== 0) {
// setStartPage(startPage - paginationSize);
// }
// }}
// className="rounded-full py-2 px-3 text-center text-sm transition-all shadow-sm hover:shadow-lg text-slate-600 hover:text-white hover:bg-lime-400 hover:border-lime-400 hover:cursor-pointer ml-2">
// <MdArrowLeft className="size-6"/>
// </button>
// {pageList.map((page) => (
// <button
// key={page}
// onClick={() => {
// setPage(page)
// }}
// className={`${pageInfo.number === page ? "bg-lime-400" : ""} min-w-9 rounded-full bg-white py-2 px-3.5 border border-transparent text-center text-sm transition-all shadow-md hover:shadow-lg focus:bg-lime-400 focus:shadow-none active:bg-lime-400 hover:bg-lime-400 active:shadow-none disabled:pointer-events-none disabled:opacity-50 disabled:shadow-none hover:cursor-pointer ml-2`}>
// {page + 1}
// </button>
// ))}
// <button
// onClick={() => {
// if (pageInfo.totalPages > startPage + paginationSize) {
// setStartPage(startPage + paginationSize);
// }
// }}
// className="min-w-9 rounded-full py-2 px-3 text-center text-sm transition-all shadow-sm hover:shadow-lg text-slate-600 hover:text-white hover:bg-lime-400 hover:border-lime-400 hover:cursor-pointer ml-2">
// <MdArrowRight className="size-6"/>
// </button>
// </div>
);


}

export default PaginationButton;
4 changes: 2 additions & 2 deletions src/components/common/TextButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ const className = " flex justify-center items-center bg-transparent py-2 px-4 ho

export function TextButton({text, onClick, addStyle}: { text: string, onClick?: () => void, addStyle?: string }) {
return (
<button onClick={onClick} className={(addStyle) ? addStyle + className : className}>
<button onClick={onClick} className={`${addStyle} ${className}`}>
{text}
</button>
);
Expand All @@ -13,7 +13,7 @@ export function TextButton({text, onClick, addStyle}: { text: string, onClick?:
export function TextLink({text, to, addStyle}: { text: string, to: string, addStyle?: string }) {

return (
<Link to={to} className={(addStyle) ? addStyle + className : className}>
<Link to={to} className={`${addStyle} ${className}`}>
{text}
</Link>
);
Expand Down
Loading