Skip to content

공통적으로 사용될 molecules 분리 #285

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

Merged
merged 12 commits into from
Aug 9, 2022
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
41 changes: 41 additions & 0 deletions src/components/common/molecules/buttons/CategorySortButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { useRouter } from "next/router";
import React from "react";

import LinkTo from "@Components/common/atom/LinkTo";

interface CategorySortButtonProps {
categoryName: string;
}

const CategorySortButton = ({ categoryName }: CategorySortButtonProps) => {
const router = useRouter();

if (router.query.category !== categoryName) {
return (
<LinkTo
scroll={false}
href={{
pathname: router.pathname,
query: { ...router.query, category: categoryName },
}}
className="p-2 text-sm font-semibold rounded-sm hover:no-underline font-eng-sub-font-2 bg-neutral-50 dark:bg-neutral-800 ring-1 ring-neutral-200 dark:ring-neutral-700 hover:bg-neutral-100 hover:dark:bg-neutral-700"
>
<span className={`text-${categoryName}`}># </span>
<span className="text-neutral-900 dark:text-neutral-50">
{categoryName}
</span>
</LinkTo>
);
}
return (
<LinkTo
scroll={false}
href={`${router.asPath.split("?")[0]}`}
className="p-2 text-sm font-semibold rounded-sm hover:no-underline font-eng-sub-font-2 bg-neutral-50 dark:bg-neutral-800 ring-1 ring-neutral-200 dark:ring-neutral-700 hover:bg-neutral-100 hover:dark:bg-neutral-700"
>
<span className={`text-${categoryName}`}># {categoryName}</span>
</LinkTo>
);
};

export default CategorySortButton;
17 changes: 17 additions & 0 deletions src/components/common/molecules/buttons/LoginButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import React from "react";

import { SiGithub } from "react-icons/si";

import PrimaryButton from "@Components/common/atom/buttons/PrimaryButton";
import useLogin from "@Components/common/molecules/buttons/hooks/useLogin";

const LoginButton = () => {
const handleLogin = useLogin();
return (
<PrimaryButton type="button" onClick={handleLogin}>
<span className="text-neutral-50">Login With Github</span>
<SiGithub className="inline mb-1 ml-3 text-white w-7 h-7" />
</PrimaryButton>
);
};
export default LoginButton;
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import useLogout from "@Components/molecules/button/hooks/useLogout";
import LOGOUT from "@Constants/text/logout";
import useLogout from "@Components/common/molecules/buttons/hooks/useLogout";

const LogoutButton = () => {
interface LogoutButtonProps {
children: string;
}

const LogoutButton = ({ children }: LogoutButtonProps) => {
const handleLogout = useLogout();
return (
<button type="button" onClick={handleLogout}>
<span className="block px-4 py-2 text-lg text-gray-700 cursor-pointer hover:underline font-eng-sub-font-1">
{LOGOUT.ko}
{children}
</span>
</button>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { useEffect } from "react";

import { TILOG_AUTH } from "@Constants/environment";
import useGetMeQuery from "@Hooks/react-query/user/useGetMeQuery";

const useLogin = () => {
Expand All @@ -16,6 +17,13 @@ const useLogin = () => {
false
);
}, [refetch]);
return () => {
return window.open(
TILOG_AUTH,
"",
"toolbar=no, menubar=no, width=600, height=700"
);
};
};

export default useLogin;
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import { AxiosResponse } from "axios";
import { InfiniteQueryObserverResult } from "react-query";

import ExceptionInterface from "@Api/errors/interfaces";
import Spinner from "@Components/common/atom/loading/Spinner";

import { GetPostsResponseDto } from "@til-log.lab/tilog-api";

import ExceptionInterface from "@Library/api/exception/interface";

interface CardLoadingProps {
hasNextPage: boolean | undefined;
isFetchingNextPage: boolean;
Expand All @@ -21,7 +24,7 @@ const CardLoading = ({
fetchNextPage,
}: CardLoadingProps) => {
if (!hasNextPage) return null;
if (isFetchingNextPage) return <>로딩중..</>;
if (isFetchingNextPage) return <Spinner size="5" />;
return (
<button type="button" onClick={() => fetchNextPage()}>
더보기
Expand Down
53 changes: 53 additions & 0 deletions src/components/common/molecules/card/post/PostCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import LinkTo from "@Components/common/atom/LinkTo";
import CommentCounter from "@Components/common/molecules/counter/CommentCounter";
import LikeCounter from "@Components/common/molecules/counter/LikeCounter";
import PostThumbnail from "@Components/common/molecules/images/PostThumbnail";
import CategoryLink from "@Components/common/molecules/link/CategoryLink";
import DateUserProfile from "@Components/common/organisms/profile/DateUserProfile";

import { GetPostsItem } from "@til-log.lab/tilog-api";

const PostCard = ({ post }: { post: GetPostsItem }) => {
return (
<div
key={post.id}
className="flex flex-col md:flex-row bg-neutral-100 dark:bg-neutral-800 md:w-[550px] xl:w-[650px] max-w-[450px] md:max-w-[650px] w-full"
>
<div className="w-full p-4 md:py-4 md:pl-4">
<DateUserProfile
isPrivate={post.private === 1}
userId={post.user.userId}
createdAt={post.createdAt}
/>

<div className="mt-2 md:h-[120px] h-[100px]">
<LinkTo href={`/post/${post.id}`}>
<h5 className="font-bold line-clamp-2 text-neutral-800 dark:text-neutral-50">
{post.title}
</h5>
<p className="text-sm sm:line-clamp-2">
{!post.subTitle ? "" : post.subTitle}
</p>
</LinkTo>
</div>

<div className="flex items-center gap-x-2">
<CategoryLink categoryName={post.category.name} />
{/* <CommentCounter count={10} /> */}
<LikeCounter count={post.like} />
</div>
</div>
<div className="order-first w-full md:order-last">
<LinkTo href={`/post/${post.id}`} className="w-full hover:no-underline">
<PostThumbnail
id={post.id}
thumbnailUrl={post.thumbnailUrl}
title={post.title}
/>
</LinkTo>
</div>
</div>
);
};

export default PostCard;
16 changes: 16 additions & 0 deletions src/components/common/molecules/counter/CommentCounter.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { AiOutlineComment } from "react-icons/ai";

interface CommentCounterProps {
count: number;
}

const CommentCounter = ({ count }: CommentCounterProps) => {
return (
<div className="space-x-1">
<AiOutlineComment className="inline w-4 h-4 text-neutral-900 dark:text-neutral-50" />
<span className="text-xs">{count}</span>
</div>
);
};

export default CommentCounter;
28 changes: 28 additions & 0 deletions src/components/common/molecules/counter/LikeCounter.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { AiFillHeart } from "react-icons/ai";

interface LikeCounterProps {
count: number;
active?: boolean;
iconSize?: string;
fontSize?: string;
}

const LikeCounter = ({
count,
active = false,
iconSize = "4",
fontSize = "xs",
}: LikeCounterProps) => {
return (
<div className="space-x-1">
<AiFillHeart
className={`inline w-${iconSize} h-${iconSize}
${active ? "text-rose-500" : "text-neutral-900 dark:text-neutral-50"}
`}
/>
<span className={`text-${fontSize}`}>{count}</span>
</div>
);
};

export default LikeCounter;
24 changes: 24 additions & 0 deletions src/components/common/molecules/images/PostThumbnail.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import React from "react";

import EmptyThumbnail from "@Components/common/atom/images/thumbnail/EmptyThumbnail";
import Thumbnail from "@Components/common/atom/images/thumbnail/Thumbnail";
import { seededColor } from "@Library/utility/color";

import { GetPostDetailResponseDto } from "@til-log.lab/tilog-api";

interface PostImageProps {
id: GetPostDetailResponseDto["id"];
title: GetPostDetailResponseDto["title"];
thumbnailUrl: GetPostDetailResponseDto["thumbnailUrl"];
}

const PostThumbnail = ({ id, thumbnailUrl, title }: PostImageProps) => {
if (thumbnailUrl) {
return <Thumbnail thumbnailUrl={thumbnailUrl} />;
}

const color = seededColor(id);
return <EmptyThumbnail title={title} color={color} />;
};

export default PostThumbnail;
23 changes: 23 additions & 0 deletions src/components/common/molecules/link/CategoryLink.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import React from "react";

import LinkTo from "@Components/common/atom/LinkTo";

interface CategoryLinkProps {
categoryName: string;
}

const CategoryLink = ({ categoryName }: CategoryLinkProps) => {
return (
<LinkTo
href={{
pathname: "/search",
query: { category: categoryName },
}}
className={`underline decoration-${categoryName} text-sm font-semibold text-${categoryName} font-eng-sub-font-1`}
>
#<span className="text-xs">{categoryName}</span>
</LinkTo>
);
};

export default CategoryLink;
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import LinkTo from "@Components/molecules/LinkTo";
import LinkTo from "@Components/common/atom/LinkTo";

interface LogoProps {
interface LogoLinkProps {
className: string;
}

const Logo = ({ className }: LogoProps) => {
const LogoLink = ({ className }: LogoLinkProps) => {
return (
<LinkTo href="/">
<div
Expand All @@ -13,4 +13,4 @@ const Logo = ({ className }: LogoProps) => {
</LinkTo>
);
};
export default Logo;
export default LogoLink;
23 changes: 23 additions & 0 deletions src/components/common/molecules/profile/UserProfile.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import React from "react";

import AvatarImage from "@Components/common/atom/images/avatar/AvatarImage";
import EmptyAvatarImage from "@Components/common/atom/images/avatar/EmptyAvatarImage";

import { GetUserProfileResponseDto } from "@til-log.lab/tilog-api";

interface UserProfileProps {
avatar?: GetUserProfileResponseDto["avatar"];
className?: string;
}

const UserProfile = ({ avatar, className }: UserProfileProps) => {
return (
<div
className={`${className} ring-1 ring-neutral-200 dark:ring-neutral-600 rounded-full`}
>
{!avatar ? <EmptyAvatarImage /> : <AvatarImage avatar={avatar} />}
</div>
);
};

export default UserProfile;
14 changes: 14 additions & 0 deletions src/components/common/molecules/tech-icons/RenderTechIcons.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import techIconsProvider from "./TechIcons";

interface Props {
categoryName: string;
}
const RenderTechIcons = ({ categoryName }: Props) => {
const upperCaseCategoryName = categoryName.toUpperCase();

if (upperCaseCategoryName in techIconsProvider) {
return techIconsProvider[upperCaseCategoryName];
}
return techIconsProvider.UNKNOWN;
};
export default RenderTechIcons;
31 changes: 31 additions & 0 deletions src/components/common/molecules/tech-icons/TechIcons.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { ReactElement } from "react";

import * as BS from "react-icons/bs";
import * as FA from "react-icons/fa";
import * as GR from "react-icons/gr";
import * as SI from "react-icons/si";

interface TechIconsInterface {
[techName: string]: ReactElement;
}

const TechIcons: TechIconsInterface = {
JAVASCRIPT: <SI.SiJavascript className="inline text-javascript" />,
TYPESCRIPT: <SI.SiTypescript className="inline text-typescript" />,
HTML: <SI.SiHtml5 className="inline text-html" />,
GITHUB: <SI.SiGithub className="inline" />,
NESTJS: <SI.SiNestjs className="inline text-nestjs" />,
REACT: <SI.SiReact className="inline text-react" />,
SWIFT: <SI.SiSwift className="inline text-swift" />,
DATABASE: <FA.FaDatabase className="inline text-database" />,
MYSQL: <GR.GrMysql className="inline text-mysql" />,
AWS: <SI.SiAmazonaws className="inline text-aws" />,
DOCKER: <SI.SiDocker className="inline text-docker" />,
DOCKER_COMPOSE: <SI.SiDocker className="inline text-docker" />,
REDIS: <SI.SiRedis className="inline text-redis " />,
NODEJS: <FA.FaNodeJs className="inline text-nodejs" />,
CSS: <SI.SiCss3 className="inline text-css" />,
INFRA: <BS.BsFillHddNetworkFill className="inline" />,
UNKNOWN: <BS.BsFillPatchQuestionFill className="inline" />,
};
export default TechIcons;
31 changes: 31 additions & 0 deletions src/components/common/molecules/text-area/TiptapEditor.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { EditorContent, EditorOptions } from "@tiptap/react";

import { useFormContext } from "react-hook-form";

import { MARKDOWN_CONTENT } from "@Constants/text/writer";

import { CreatePostRequestBodyDto } from "@til-log.lab/tilog-api";

import useTiptapEditor from "./hooks/useTiptapEditor";

interface TiptapEditorProps {
content?: EditorOptions["content"];
}

const TiptapEditor = ({ content = null }: TiptapEditorProps) => {
const { register } = useFormContext<CreatePostRequestBodyDto>();
const tiptapEditor = useTiptapEditor(content);
if (!tiptapEditor) return null;

return (
<EditorContent
{...register(MARKDOWN_CONTENT, {
required: true,
})}
className="z-0 h-[1000px] p-5 bg-white dark:bg-neutral-800"
editor={tiptapEditor}
/>
);
};

export default TiptapEditor;
Loading