From 19f808bc0d32c7d8fd1ec2790461ec0001555bf1 Mon Sep 17 00:00:00 2001 From: Kim0426 <706shin1728@naver.com> Date: Fri, 22 Dec 2023 01:42:21 +0900 Subject: [PATCH 1/5] chore: init upload project --- .eslintrc.json | 68 +++++++++ .gitignore | 67 +++++++++ next.config.js | 29 ++++ package.json | 73 ++++++++++ src copy/@types/AboutProps.ts | 6 + src copy/@types/ButtonType.ts | 10 ++ src copy/@types/CategoryType.ts | 4 + src copy/@types/GuestBookType.ts | 3 + src copy/@types/PostType.ts | 0 src copy/@types/ThemeType.ts | 1 + src copy/Component/About/Content.tsx | 22 +++ .../Component/About/ProfileImgWrapper.tsx | 18 +++ src copy/Component/About/Title.tsx | 20 +++ src copy/Component/Blog/CodeBlock.tsx | 35 +++++ src copy/Component/Blog/Exterct.tsx | 20 +++ src copy/Component/Blog/LayOut.tsx | 78 +++++++++++ src copy/Component/Blog/PostSearchModal.tsx | 94 +++++++++++++ src copy/Component/Blog/SearchPost.tsx | 35 +++++ .../Component/CategoryList/CategoryList.tsx | 43 ++++++ src copy/Component/Common/Avatar.tsx | 0 src copy/Component/Common/Button.tsx | 43 ++++++ src copy/Component/Common/Footer.tsx | 51 +++++++ src copy/Component/Common/Navbar.tsx | 61 ++++++++ src copy/Component/Common/PostLayout.tsx | 32 +++++ src copy/Component/DarkMode/ToggoeButton.tsx | 34 +++++ src copy/Component/GA/GA.tsx | 37 +++++ src copy/Component/Giscus/Gitcus.tsx | 22 +++ src copy/Component/GuestBook/GuestBook.tsx | 36 +++++ src copy/Component/Input/Input.tsx | 17 +++ src copy/Component/Input/InputBox.tsx | 21 +++ src copy/Component/Input/index.tsx | 4 + .../Component/Notion/NotionresumeClient.tsx | 40 ++++++ src copy/Component/Post/PostItem.tsx | 40 ++++++ src copy/Component/Post/PostList.tsx | 24 ++++ src copy/Component/TOC/index.tsx | 58 ++++++++ src copy/app/(root)/(routes)/(home)/page.tsx | 35 +++++ src copy/app/(root)/(routes)/about/page.tsx | 46 ++++++ .../app/(root)/(routes)/blog/[slug]/page.tsx | 132 ++++++++++++++++++ .../category/[categoryId]/[slug]/page.tsx | 101 ++++++++++++++ .../(routes)/category/[categoryId]/page.tsx | 63 +++++++++ .../(routes)/guestbook/HydratedGuestBook.tsx | 16 +++ .../app/(root)/(routes)/guestbook/page.tsx | 5 + src copy/app/(root)/(routes)/notion/page.tsx | 27 ++++ .../(root)/(routes)/notion/resume/page.tsx | 12 ++ src copy/app/(root)/layout.tsx | 96 +++++++++++++ src copy/app/(root)/not-found.tsx | 3 + src copy/app/Recoil.tsx | 7 + src copy/app/api/guestbook/route.ts | 58 ++++++++ src copy/app/api/slugs/[id]/route.ts | 29 ++++ src copy/app/api/slugs/route.ts | 19 +++ src copy/app/firebase.js | 17 +++ src copy/app/globalAtom.ts | 20 +++ src copy/app/globals.css | 25 ++++ src copy/app/queryClientProvider.tsx | 28 ++++ src copy/app/registry.tsx | 28 ++++ src copy/app/sitemap.ts | 27 ++++ src copy/app/sitemap.xml.tsx | 1 + src copy/app/themeWrapper.tsx | 22 +++ src copy/constants/POST.ts | 7 + src copy/hooks/useGetGuestBook.ts | 20 +++ src copy/hooks/useInput.ts | 21 +++ src copy/hooks/useNaverInit.ts | 29 ++++ src copy/hooks/useObserver.ts | 42 ++++++ src copy/hooks/usePostGuestBook.ts | 17 +++ src copy/hooks/usePostQuery.ts | 40 ++++++ src copy/hooks/useScroll.tsx | 34 +++++ src copy/hooks/useSearchPost.tsx | 38 +++++ src copy/hooks/useTheme.ts | 13 ++ src copy/style/globalStyle.tsx | 48 +++++++ src copy/style/theme/darkMode.ts | 26 ++++ src copy/style/theme/media.ts | 17 +++ src copy/utils/axiosClient.ts | 46 ++++++ src copy/utils/getQueryClient.ts | 8 ++ tsconfig.json | 34 +++++ 74 files changed, 2403 insertions(+) create mode 100644 .eslintrc.json create mode 100644 .gitignore create mode 100644 next.config.js create mode 100644 package.json create mode 100644 src copy/@types/AboutProps.ts create mode 100644 src copy/@types/ButtonType.ts create mode 100644 src copy/@types/CategoryType.ts create mode 100644 src copy/@types/GuestBookType.ts create mode 100644 src copy/@types/PostType.ts create mode 100644 src copy/@types/ThemeType.ts create mode 100644 src copy/Component/About/Content.tsx create mode 100644 src copy/Component/About/ProfileImgWrapper.tsx create mode 100644 src copy/Component/About/Title.tsx create mode 100644 src copy/Component/Blog/CodeBlock.tsx create mode 100644 src copy/Component/Blog/Exterct.tsx create mode 100644 src copy/Component/Blog/LayOut.tsx create mode 100644 src copy/Component/Blog/PostSearchModal.tsx create mode 100644 src copy/Component/Blog/SearchPost.tsx create mode 100644 src copy/Component/CategoryList/CategoryList.tsx create mode 100644 src copy/Component/Common/Avatar.tsx create mode 100644 src copy/Component/Common/Button.tsx create mode 100644 src copy/Component/Common/Footer.tsx create mode 100644 src copy/Component/Common/Navbar.tsx create mode 100644 src copy/Component/Common/PostLayout.tsx create mode 100644 src copy/Component/DarkMode/ToggoeButton.tsx create mode 100644 src copy/Component/GA/GA.tsx create mode 100644 src copy/Component/Giscus/Gitcus.tsx create mode 100644 src copy/Component/GuestBook/GuestBook.tsx create mode 100644 src copy/Component/Input/Input.tsx create mode 100644 src copy/Component/Input/InputBox.tsx create mode 100644 src copy/Component/Input/index.tsx create mode 100644 src copy/Component/Notion/NotionresumeClient.tsx create mode 100644 src copy/Component/Post/PostItem.tsx create mode 100644 src copy/Component/Post/PostList.tsx create mode 100644 src copy/Component/TOC/index.tsx create mode 100644 src copy/app/(root)/(routes)/(home)/page.tsx create mode 100644 src copy/app/(root)/(routes)/about/page.tsx create mode 100644 src copy/app/(root)/(routes)/blog/[slug]/page.tsx create mode 100644 src copy/app/(root)/(routes)/category/[categoryId]/[slug]/page.tsx create mode 100644 src copy/app/(root)/(routes)/category/[categoryId]/page.tsx create mode 100644 src copy/app/(root)/(routes)/guestbook/HydratedGuestBook.tsx create mode 100644 src copy/app/(root)/(routes)/guestbook/page.tsx create mode 100644 src copy/app/(root)/(routes)/notion/page.tsx create mode 100644 src copy/app/(root)/(routes)/notion/resume/page.tsx create mode 100644 src copy/app/(root)/layout.tsx create mode 100644 src copy/app/(root)/not-found.tsx create mode 100644 src copy/app/Recoil.tsx create mode 100644 src copy/app/api/guestbook/route.ts create mode 100644 src copy/app/api/slugs/[id]/route.ts create mode 100644 src copy/app/api/slugs/route.ts create mode 100644 src copy/app/firebase.js create mode 100644 src copy/app/globalAtom.ts create mode 100644 src copy/app/globals.css create mode 100644 src copy/app/queryClientProvider.tsx create mode 100644 src copy/app/registry.tsx create mode 100644 src copy/app/sitemap.ts create mode 100644 src copy/app/sitemap.xml.tsx create mode 100644 src copy/app/themeWrapper.tsx create mode 100644 src copy/constants/POST.ts create mode 100644 src copy/hooks/useGetGuestBook.ts create mode 100644 src copy/hooks/useInput.ts create mode 100644 src copy/hooks/useNaverInit.ts create mode 100644 src copy/hooks/useObserver.ts create mode 100644 src copy/hooks/usePostGuestBook.ts create mode 100644 src copy/hooks/usePostQuery.ts create mode 100644 src copy/hooks/useScroll.tsx create mode 100644 src copy/hooks/useSearchPost.tsx create mode 100644 src copy/hooks/useTheme.ts create mode 100644 src copy/style/globalStyle.tsx create mode 100644 src copy/style/theme/darkMode.ts create mode 100644 src copy/style/theme/media.ts create mode 100644 src copy/utils/axiosClient.ts create mode 100644 src copy/utils/getQueryClient.ts create mode 100644 tsconfig.json diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 0000000..6db3279 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,68 @@ +{ + "plugins": ["import"], + "extends": ["next/core-web-vitals", "plugin:import/recommended"], + "rules": { + "no-unused-vars": [ + "warn", + { + "argsIgnorePattern": "^_", + "varsIgnorePattern": "^_" + } + ], + "import/named": 0, + "import/order": [ + "error", + { + "newlines-between": "always", + "groups": [ + "type", + "builtin", + "external", + "internal", + "parent", + "sibling", + "index", + "unknown" + ], + "pathGroups": [ + { + "pattern": "next*", + "group": "external", + "position": "before" + }, + { + "pattern": "react*", + "group": "external", + "position": "before" + }, + { + "pattern": "src/@hooks/**", + "group": "internal", + "position": "after" + }, + { + "pattern": "src/@Component/**", + "group": "internal", + "position": "after" + }, + { + "pattern": "src/@utils/**", + "group": "internal", + "position": "after" + } + ], + "pathGroupsExcludedImportTypes": [ + "next*", + "react*", + "src/@hooks/**", + "src/@Component/**", + "src/@utils/**" + ], + "alphabetize": { + "order": "asc", + "caseInsensitive": true + } + } + ] + } +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b848d1d --- /dev/null +++ b/.gitignore @@ -0,0 +1,67 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# lighthouseci +.lighthouseci +/lhci_reports/ + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +.env + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# local env files +.env*.local + +# vercel +.vercel + +.lighthouserc.js +.commitlint.config.js + +# typescript +*.tsbuildinfo +next-env.d.ts + +# Sentry Auth Token +.sentryclirc +.sentry.client.config.ts +.sentry.edge.config.ts +.sentry.server.config.ts + +.tsconfig.json + +public + +posts +.next +.vercel +.lighthouseci +.husky + +lhci_reports +.vscode +# package.json +package-lock.json + +public diff --git a/next.config.js b/next.config.js new file mode 100644 index 0000000..25e8879 --- /dev/null +++ b/next.config.js @@ -0,0 +1,29 @@ +const withBundleAnalyzer = require('@next/bundle-analyzer')({ + enabled: process.env.ANALYZE === 'true', +}); + +const nextConfig = { + experimental: { + optimizePackageImports: ['styled-components,sentry,lodash,react-markdown'], + }, + reactStrictMode: true, + compiler: { + styledComponents: true, + }, + images: { + remotePatterns: [ + { + protocol: 'https', + hostname: '**', + }, + ], + formats: ['image/avif', 'image/webp'], + }, +}; + +const bundleAnalyzerConfig = withBundleAnalyzer({ + ...nextConfig, + compress: true, +}); + +module.exports = bundleAnalyzerConfig; diff --git a/package.json b/package.json new file mode 100644 index 0000000..3d18104 --- /dev/null +++ b/package.json @@ -0,0 +1,73 @@ +{ + "name": "hj-devlog", + "version": "0.1.0", + "private": true, + "homepage": "https://github.com/khj0426/HJ_Devlog", + "scripts": { + "lhci": "next build && lhci autorun", + "dev": "next dev", + "build": "next build", + "start": "next start", + "lint": "next lint", + "lint:fix": "next lint --fix", + "deploy": "next build && npx gh-pages -d out", + "analyze": "cross-env ANALYZE=true next build", + "analyze:server": "cross-env BUNDLE_ANALYZE=server next build", + "analyze:browser": "cross-env BUNDLE_ANALYZE=browser next build" + }, + "dependencies": { + "@giscus/react": "^2.2.8", + "@next/bundle-analyzer": "^13.4.7", + "@sentry/nextjs": "^7.64.0", + "@tanstack/react-query": "^4.33.0", + "@types/node": "20.2.5", + "@types/react": "18.2.8", + "@types/react-dom": "18.2.4", + "@types/react-syntax-highlighter": "^15.5.7", + "@types/recoil": "^0.0.9", + "@types/styled-components": "^5.1.26", + "@types/uuid": "^9.0.1", + "autoprefixer": "10.4.14", + "axios": "^1.6.2", + "cross-env": "^7.0.3", + "date-fns": "^2.30.0", + "eslint": "8.42.0", + "eslint-config-next": "13.4.4", + "firebase": "^10.7.1", + "github-label-sync": "^2.3.1", + "gray-matter": "^4.0.3", + "js-cookie": "^3.0.5", + "next": "^13.5.0", + "notion-client": "^6.16.0", + "postcss": "8.4.24", + "prism-react-renderer": "^2.0.5", + "react": "18.2.0", + "react-cookie": "^6.1.1", + "react-dom": "18.2.0", + "react-markdown": "^8.0.7", + "react-notion-x": "^6.16.0", + "react-secure-storage": "^1.3.0", + "react-syntax-highlighter": "^15.5.0", + "recoil": "^0.7.7", + "rehype-raw": "^6.1.1", + "remark": "^14.0.3", + "remark-html": "^15.0.2", + "styled-components": "^6.0.0-rc.3", + "tailwindcss": "3.3.2", + "typescript": "5.1.3", + "uuid": "^9.0.0" + }, + "devDependencies": { + "@commitlint/cli": "^17.7.2", + "@commitlint/config-conventional": "^17.7.0", + "@lhci/cli": "^0.12.0", + "@sentry/utils": "^7.64.0", + "@types/glob": "^8.1.0", + "@typescript-eslint/parser": "^6.7.5", + "eslint-import-resolver-typescript": "^3.6.1", + "eslint-plugin-import": "^2.28.1", + "gh-pages": "^5.0.0", + "glob": "^10.2.7", + "husky": "^8.0.3" + } +} diff --git a/src copy/@types/AboutProps.ts b/src copy/@types/AboutProps.ts new file mode 100644 index 0000000..9ae002c --- /dev/null +++ b/src copy/@types/AboutProps.ts @@ -0,0 +1,6 @@ +type AboutProps = { + title: string; + imgurl: string; + content: string | string[]; +}; +export default AboutProps; diff --git a/src copy/@types/ButtonType.ts b/src copy/@types/ButtonType.ts new file mode 100644 index 0000000..a3dc9a7 --- /dev/null +++ b/src copy/@types/ButtonType.ts @@ -0,0 +1,10 @@ +import { CSSProperties } from 'react'; + +export type ButtonProps = { + style?: CSSProperties; + disabled?: boolean; + variant?: 'default' | 'outlined'; + label?: string; + icon?: React.ReactNode; + onClick?: () => void; +}; diff --git a/src copy/@types/CategoryType.ts b/src copy/@types/CategoryType.ts new file mode 100644 index 0000000..18cac1d --- /dev/null +++ b/src copy/@types/CategoryType.ts @@ -0,0 +1,4 @@ +export type CategoryItem = { + category: string; + categoryCount: string; +}; diff --git a/src copy/@types/GuestBookType.ts b/src copy/@types/GuestBookType.ts new file mode 100644 index 0000000..bcf688e --- /dev/null +++ b/src copy/@types/GuestBookType.ts @@ -0,0 +1,3 @@ +export type GuestBook = { + comments: {}; +}; diff --git a/src copy/@types/PostType.ts b/src copy/@types/PostType.ts new file mode 100644 index 0000000..e69de29 diff --git a/src copy/@types/ThemeType.ts b/src copy/@types/ThemeType.ts new file mode 100644 index 0000000..d2c2a51 --- /dev/null +++ b/src copy/@types/ThemeType.ts @@ -0,0 +1 @@ +export type ThemeType = 'light' | 'dark'; diff --git a/src copy/Component/About/Content.tsx b/src copy/Component/About/Content.tsx new file mode 100644 index 0000000..8f05252 --- /dev/null +++ b/src copy/Component/About/Content.tsx @@ -0,0 +1,22 @@ +import styled from 'styled-components'; +import { v4 as uuidv4 } from 'uuid'; + +const StyledContent = styled.section` + display: flex; + min-width: 300px; + min-height: 400px; + max-width: 400px; + max-height: 500px; + display: flex; + font-size: 18px; + flex-direction: column; +`; +export default function Content({ content }: { content: string | string[] }) { + return ( + + {Array.from(content).map((eachContent) => ( +

{eachContent}

+ ))} +
+ ); +} diff --git a/src copy/Component/About/ProfileImgWrapper.tsx b/src copy/Component/About/ProfileImgWrapper.tsx new file mode 100644 index 0000000..405c361 --- /dev/null +++ b/src copy/Component/About/ProfileImgWrapper.tsx @@ -0,0 +1,18 @@ +'use client'; + +import Image from 'next/image'; +export default function ProfileImageWrapper({ imgurl }: { imgurl: string }) { + return ( +
+ About 페이지 프로필 이미지 +
+ ); +} diff --git a/src copy/Component/About/Title.tsx b/src copy/Component/About/Title.tsx new file mode 100644 index 0000000..f0d00e8 --- /dev/null +++ b/src copy/Component/About/Title.tsx @@ -0,0 +1,20 @@ +'use client'; + +import styled from 'styled-components'; + +const StyledTitle = styled.h3` + font-weight: 700; + display: flex; + flex-wrap: wrap; + + @media ${({ theme }) => theme.device.tablet} { + font-size: 24px; + } + @media ${({ theme }) => theme.device.mobile} { + font-size: 20px; + } +`; + +export default function Title({ title }: { title: string }) { + return {title}; +} diff --git a/src copy/Component/Blog/CodeBlock.tsx b/src copy/Component/Blog/CodeBlock.tsx new file mode 100644 index 0000000..e161c0d --- /dev/null +++ b/src copy/Component/Blog/CodeBlock.tsx @@ -0,0 +1,35 @@ +'use client'; +import dynamic from 'next/dynamic'; +const PrismLight = dynamic( + () => import('react-syntax-highlighter/dist/cjs/prism-light') +); +import { materialDark } from 'react-syntax-highlighter/dist/esm/styles/prism'; + + +export default function CodeBlock({ + children +}: { + children: string | string[]; +}) { + return ( + + {children} + + ); +} diff --git a/src copy/Component/Blog/Exterct.tsx b/src copy/Component/Blog/Exterct.tsx new file mode 100644 index 0000000..d7e1b5a --- /dev/null +++ b/src copy/Component/Blog/Exterct.tsx @@ -0,0 +1,20 @@ +'use client'; +import styled from 'styled-components'; + +const StyledPostExterct = styled.h5` + color: #808080; + @media ${({ theme }) => theme.device.laptop} { + font-size: 14px; + } + + @media ${({ theme }) => theme.device.tablet} { + font-size: 13px; + } + @media ${({ theme }) => theme.device.mobile} { + font-size: 11px; + } +`; + +export default function PostExterct({ exterct }: { exterct: string }) { + return {exterct}; +} diff --git a/src copy/Component/Blog/LayOut.tsx b/src copy/Component/Blog/LayOut.tsx new file mode 100644 index 0000000..224db2f --- /dev/null +++ b/src copy/Component/Blog/LayOut.tsx @@ -0,0 +1,78 @@ +'use client'; +import { useEffect } from 'react'; + +import styled, { css } from 'styled-components'; + +const PostLayOutPC = css` + min-width: 60%; + max-width: 60%; +`; +const PostLayOutMobile = css` + min-width: 80%; + max-width: 80%; +`; + +const StyledPostLayOut = styled.article` + ${PostLayOutPC} + display: flex; + margin: 20px auto; + flex-direction: column; + margin-bottom: 50px; + font-size: 20px; + + @media ${({ theme }) => theme.device.laptop} { + ${PostLayOutPC} + } + + @media ${({ theme }) => theme.device.tablet} { + ${PostLayOutPC} + } + + @media ${({ theme }) => theme.device.mobile} { + ${PostLayOutMobile} + } +`; + +export default function BlogLayOut({ + children, +}: { + children: React.ReactNode[]; +}) { + useEffect(() => { + const getH2Element = Array.from(document.querySelectorAll('h2')); + const getH3Element = Array.from(document.querySelectorAll('h3')); + + const getAllHeaderElement = [...getH2Element, ...getH3Element]; + + const io = new IntersectionObserver( + (entries) => { + entries.forEach((entry) => { + const getTOC = entry.target.textContent as string; + const getTOCElement = document.getElementsByClassName( + entry.target.textContent as string + ); + if (!getTOCElement[0]) { + return; + } + if (getTOCElement && entry.isIntersecting) { + getTOCElement[0].classList.add('active'); + } else if (getTOCElement && !entry.isIntersecting) { + getTOCElement[0].classList.remove('active'); + } + }); + }, + { + threshold: 0.4, + rootMargin: '-50px 0px 0px 0px', + } + ); + + getAllHeaderElement.forEach((eachHeaderElement) => { + io.observe(eachHeaderElement); + }); + + return () => io.disconnect(); + }, []); + + return {children}; +} diff --git a/src copy/Component/Blog/PostSearchModal.tsx b/src copy/Component/Blog/PostSearchModal.tsx new file mode 100644 index 0000000..f2f85d1 --- /dev/null +++ b/src copy/Component/Blog/PostSearchModal.tsx @@ -0,0 +1,94 @@ +import { ChangeEvent, useState } from 'react'; + +import Link from 'next/link'; +import styled from 'styled-components'; + +import useSearchPost from '@/hooks/useSearchPost'; + +const StyledPostSearchModalWrapper = styled.div` + position: fixed; + top: 0; + left: 0; + backdrop-filter: blur(1px); + width: 100%; + height: 100%; +`; + +const StyledPostSearchInput = styled.input` + padding: 14px 24px; + width: 100%; + height: 100px; + box-sizing: border-box; + outline: none; + border: none; + background-color: inherit; + color: rgb(255, 255, 255); + font-family: inherit; +`; + +const StyledPostSearchModal = styled.div` + width: 350px; + min-height: 450px; + display: flex; + flex-direction: column; + align-items: center; + height: auto; + position: absolute; + background-color: rgb(38, 41, 43); + top: 50%; + left: 50%; + transform: translate(-50%, -50%) scale(1); + color: rgb(236, 237, 238); + box-shadow: 0 15px 30px 0 rgba(#000, 0.25); + border-radius: 15px; + overflow: auto; +`; + +export default function PostSearchModal({ + onCloseModal, +}: { + onCloseModal: () => void; +}) { + const [querySearch, setQuerySearch] = useState(null); + const { posts } = useSearchPost(querySearch as string); + + const handleChangSearchQuery = (e: ChangeEvent) => { + if (e.target.value.length === 0) { + return; + } + + setQuerySearch(e.target.value); + }; + + return ( + onCloseModal()}> + +

onCloseModal()} + style={{ + cursor: 'pointer', + }} + > + X +

+ + {posts.map((post) => ( + onCloseModal()} + style={{ + color: 'inherit', + }} + > + {post.title} + + ))} +
+
+ ); +} diff --git a/src copy/Component/Blog/SearchPost.tsx b/src copy/Component/Blog/SearchPost.tsx new file mode 100644 index 0000000..b2837d9 --- /dev/null +++ b/src copy/Component/Blog/SearchPost.tsx @@ -0,0 +1,35 @@ +import Image from 'next/image'; +import { useRecoilState } from 'recoil'; + +import { postSearchModalState } from '@/app/globalAtom'; + +import SearchImage from '../../.././public/images/search.webp'; + +import PostSearchModal from './PostSearchModal'; + +export default function SearchPostButton() { + const [modalState, setPostSearchModal] = useRecoilState(postSearchModalState); + + return ( + <> + 블로그 글 검색 이미지 { + setPostSearchModal(!modalState); + }} + > + {modalState && ( + setPostSearchModal(!modalState)} /> + )} + + ); +} diff --git a/src copy/Component/CategoryList/CategoryList.tsx b/src copy/Component/CategoryList/CategoryList.tsx new file mode 100644 index 0000000..56f1a6d --- /dev/null +++ b/src copy/Component/CategoryList/CategoryList.tsx @@ -0,0 +1,43 @@ +'use client'; + +import type { CategoryItem } from '@/@types/CategoryType'; + +import Link from 'next/link'; +import styled from 'styled-components'; + +const CategoryListStyle = styled.span` + color: ${({ theme }) => theme.text}; + font-size: 20px; + cursor: pointer; + &:hover { + text-decoration: underline ${({ theme }) => theme.text}; + } +`; + +const CategoryListWrapper = styled.div` + gap: 15px; + display: flex; + width: 100%; + flex-wrap: wrap; + flex-direction: row; + justify-content: center; +`; +export default function CategoryList({ + category, +}: { + category: CategoryItem[]; +}) { + return ( + + {category.map(({ category, categoryCount }) => { + return ( + + + # {category + categoryCount} + + + ); + })} + + ); +} diff --git a/src copy/Component/Common/Avatar.tsx b/src copy/Component/Common/Avatar.tsx new file mode 100644 index 0000000..e69de29 diff --git a/src copy/Component/Common/Button.tsx b/src copy/Component/Common/Button.tsx new file mode 100644 index 0000000..afe9a2f --- /dev/null +++ b/src copy/Component/Common/Button.tsx @@ -0,0 +1,43 @@ +import type { ButtonProps } from '@/@types/ButtonType'; + +import styled from 'styled-components'; + +const StyledBaseButton = styled.button` + background-color: ${({ theme }) => theme.backgroundPost}; + font-weight: bold; + border-radius: 0.25rem; + cursor: pointer; + &:hover { + background-color: rgba(66, 153, 225, 0.7); + } + &:disabled { + cursor: not-allowed; + opacity: 0.5; + } + color: ${({ theme }) => theme.text}; + transition: icon 0.3s ease-in-out; + border: ${(props) => + props.variant === 'outlined' ? '1px rgba(255,255,255,1)' : 'none'}; +`; + +export default function Button({ + variant, + style, + disabled, + icon, + onClick, + label, +}: ButtonProps) { + return ( + + {icon} + {label} + + ); +} diff --git a/src copy/Component/Common/Footer.tsx b/src copy/Component/Common/Footer.tsx new file mode 100644 index 0000000..6e55496 --- /dev/null +++ b/src copy/Component/Common/Footer.tsx @@ -0,0 +1,51 @@ +'use client'; + +import Image from 'next/image'; +import Link from 'next/link'; +import styled from 'styled-components'; + +import InstagramImage from '../../../public/images/Instagramgram.webp'; + +const StyledFooter = styled.footer` + height: 30px; + display: flex; + flex-direction: column; + align-items: center; + margin: 60px auto; +`; + +const StyledLinkIconArea = styled.div` + display: flex; + justify-content: center; + gap: 10px; + margin: 0 auto; +`; + +export default function Footer() { + return ( + + + + + + + + + 인스타그램 주소 + + +
HJ DevLog
+
©{new Date().getFullYear()} 효중킴의 블로그, Powered By Next.js
+
+ ); +} diff --git a/src copy/Component/Common/Navbar.tsx b/src copy/Component/Common/Navbar.tsx new file mode 100644 index 0000000..07b0808 --- /dev/null +++ b/src copy/Component/Common/Navbar.tsx @@ -0,0 +1,61 @@ +'use client'; +import Link from 'next/link'; +import styled, { css } from 'styled-components'; + +import SearchPostButton from '../Blog/SearchPost'; +import ToggleDarkModeButton from '../DarkMode/ToggoeButton'; + +const StyledNavBarLayout = styled.nav` + position: sticky; + top: 0; + left: 0; + width: 100%; + display: flex; + font-size: 20px; + color: ${({ theme }) => theme.text}; + gap: 15px; + margin: 0 auto; + background-color: ${({ theme }) => theme.body}; + align-items: center; + justify-content: space-around; + z-index: 1; +`; + +const StyledNavBarTitle = styled(Link)` + font-weight: 600; + text-decoration: none; + color: inherit; + ${({ href }) => + href === '/notion/resume' && + css` + @media (max-width: 1024px) { + opacity: 0; + } + `} +`; + +export default function Navbar() { + return ( + +
+ Blog + About + Resume +
+
+ + +
+
+ ); +} diff --git a/src copy/Component/Common/PostLayout.tsx b/src copy/Component/Common/PostLayout.tsx new file mode 100644 index 0000000..559ef01 --- /dev/null +++ b/src copy/Component/Common/PostLayout.tsx @@ -0,0 +1,32 @@ +'use client'; +import styled from 'styled-components'; +const StyledPostLayOut = styled.section` + display: flex; + justify-content: space-between; + padding-left: 5px; + border-radius: 25px; + margin: 25px auto; + background: ${({ theme }) => theme.backgroundPost}; + max-width: 800px; + width: 60%; + + @media ${({ theme }) => theme.device.laptop} { + width: 80%; + } + + @media ${({ theme }) => theme.device.tablet} { + width: 90%; + } + + @media ${({ theme }) => theme.device.mobile} { + width: 90%; + } +`; + +export default function PostLayout({ + children, +}: { + children: React.ReactNode | React.ReactNode[]; +}) { + return {children}; +} diff --git a/src copy/Component/DarkMode/ToggoeButton.tsx b/src copy/Component/DarkMode/ToggoeButton.tsx new file mode 100644 index 0000000..99b2357 --- /dev/null +++ b/src copy/Component/DarkMode/ToggoeButton.tsx @@ -0,0 +1,34 @@ +import Image from 'next/image'; +import { useRecoilState } from 'recoil'; + +import { themeState } from '@/app/globalAtom'; + +import darkModeImage from '../../../public/images/darkmode.webp'; +import lightModeImage from '../../../public/images/lightmode.webp'; + +export default function ToggleDarkModeButton() { + const [currentTheme, setCurrentTheme] = useRecoilState(themeState); + const modeImageSrc = + currentTheme === 'light' ? darkModeImage : lightModeImage; + + const handleClickToggleImage = () => { + if (currentTheme === 'light') { + setCurrentTheme('dark'); + return; + } + setCurrentTheme('light'); + }; + return ( + 모드를 바꾸는 이미지 + ); +} diff --git a/src copy/Component/GA/GA.tsx b/src copy/Component/GA/GA.tsx new file mode 100644 index 0000000..f9b261d --- /dev/null +++ b/src copy/Component/GA/GA.tsx @@ -0,0 +1,37 @@ +'use client'; +import Script from 'next/script'; + +const GoogleAnalytics = ({ + GA_MEASUREMENT_ID, +}: { + GA_MEASUREMENT_ID: string; +}) => { + return ( + <> +