From e2169588845fb65d318e2a9e7518f66898731e1c Mon Sep 17 00:00:00 2001 From: JaeSeoKim Date: Sat, 16 Apr 2022 22:52:55 +0900 Subject: [PATCH] =?UTF-8?q?feat(blog):=20=EB=B8=94=EB=A1=9C=EA=B7=B8=20?= =?UTF-8?q?=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8(header)=20=EC=9E=91?= =?UTF-8?q?=EC=97=85=20=EC=A4=91=20#35?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../blog/components/Header/Header.stories.tsx | 24 ++- .../blog/components/Header/Header.styled.tsx | 61 +++++--- packages/blog/components/Header/Header.tsx | 142 ++++++++---------- .../components/Header/Hero/Hero.stories.tsx | 65 ++++++++ .../components/Header/Hero/Hero.styled.tsx | 72 +++++++++ packages/blog/components/Header/Hero/Hero.tsx | 30 ++++ .../blog/components/Header/Hero/index.tsx | 2 + packages/blog/pages/index.tsx | 30 ++-- packages/blog/pages/posts/[slug].tsx | 9 +- packages/blog/styles/theme.ts | 8 + 10 files changed, 332 insertions(+), 111 deletions(-) create mode 100644 packages/blog/components/Header/Hero/Hero.stories.tsx create mode 100644 packages/blog/components/Header/Hero/Hero.styled.tsx create mode 100644 packages/blog/components/Header/Hero/Hero.tsx create mode 100644 packages/blog/components/Header/Hero/index.tsx diff --git a/packages/blog/components/Header/Header.stories.tsx b/packages/blog/components/Header/Header.stories.tsx index 331a2ca..00fab0e 100644 --- a/packages/blog/components/Header/Header.stories.tsx +++ b/packages/blog/components/Header/Header.stories.tsx @@ -13,13 +13,31 @@ export default { const Template: ComponentStory = (args) => (
) -export const Primary = Template.bind({}) -Primary.args = {} +export const Home = Template.bind({}) +Home.args = {} +Home.parameters = { + nextRouter: { + pathname: '/', + asPath: '/', + query: {}, + }, +} + +export const OtherPage = Template.bind({}) +OtherPage.args = {} +OtherPage.parameters = { + nextRouter: { + pathname: '/other-page', + asPath: '/other-page', + query: {}, + }, +} diff --git a/packages/blog/components/Header/Header.styled.tsx b/packages/blog/components/Header/Header.styled.tsx index 0853ab2..a971da1 100644 --- a/packages/blog/components/Header/Header.styled.tsx +++ b/packages/blog/components/Header/Header.styled.tsx @@ -1,52 +1,71 @@ import { motion } from 'framer-motion' -import styled from 'styled-components' +import styled, { css } from 'styled-components' +import { breakPoint } from '../../styles/theme' + +export const mediaPadding = css` + padding: 16px 32px; +` const Styled = { rootContainer: styled(motion.header)` - position: fixed; + position: sticky; + top: 0; + left: 0; + z-index: 1; display: flex; + width: 100%; `, - mainContainer: styled(motion.div)` + wrapContainer: styled(motion.div)` display: flex; justify-content: space-between; + align-items: center; - width: 100%; - max-width: 1536px; - - padding: 8px 16px; + ${mediaPadding} + width: 100%; + max-width: 1280px; margin-left: auto; margin-right: auto; `, - leftContainer: styled(motion.div)` + flexContainer: styled(motion.div)` display: flex; align-items: center; gap: 8px; `, - image: styled(motion.img)` - border-radius: 50%; - `, - infoContainer: styled(motion.div)` + anchor: styled(motion.a)` display: flex; - flex-direction: column; - gap: 8px; + align-items: center; + gap: 4px; + + color: inherit; + text-decoration: inherit; + `, + icon: styled(motion.img)` + border-radius: 50%; + width: 32px; + height: 32px; + + display: block; + @media screen and (max-width: ${breakPoint.md}px) { + display: none; + } `, title: styled(motion.h1)` margin: 0; + margin-top: auto; + margin-bottom: auto; font-weight: 600; `, - subtitle: styled(motion.h2)` - margin: 0; - font-weight: 400; - `, - rigthContainer: styled(motion.div)` - display: flex; + menu: styled(motion.div)` + display: none; + @media screen and (max-width: ${breakPoint.md}px) { + display: flex; + } `, - search: styled(motion.div)``, } export default Styled diff --git a/packages/blog/components/Header/Header.tsx b/packages/blog/components/Header/Header.tsx index 98df04a..33261b3 100644 --- a/packages/blog/components/Header/Header.tsx +++ b/packages/blog/components/Header/Header.tsx @@ -1,20 +1,32 @@ -import React, { useState } from 'react' -import { useViewportScroll, MotionConfig } from 'framer-motion' +import React, { useEffect, useState } from 'react' +import { MotionConfig, LayoutGroup } from 'framer-motion' import Styled from './Header.styled' +import Link from 'next/link' +import Hero from './Hero' +import { useRouter } from 'next/router' + +const image = { + src: 'https://avatars.githubusercontent.com/u/48559454?v=4', + alt: "JaeSeoKim's avatar", +} + +const title = 'JaeSeoKim' +const subtitle = '🎢 To become a better developer...!' export type HeaderProps = {} const Header: React.FC = ({}) => { - const { scrollY } = useViewportScroll() - const [isHero, setIsHero] = useState(scrollY.get() < 200) + const router = useRouter() + const [isHome, setIsHome] = useState(router.pathname === '/') + const [isShowHero, setIsShowHero] = useState(false) - scrollY.onChange((value) => { - if (value < 200) { - setIsHero(true) + useEffect(() => { + if (router.pathname === '/') { + setIsHome(true) } else { - setIsHero(false) + setIsHome(false) } - }) + }, [router.pathname]) return ( = ({}) => { type: 'tween', }} > - - - - - - - JaeSeoKim - - - Frontend Developer - - - - right - - + + + + menu + + {!isShowHero && ( + + + + {title} + + + )} + + right + + + {isHome && ( + { + setIsShowHero(true) + }} + onViewportLeave={() => { + setIsShowHero(false) + }} + pathname={router.pathname} + /> + )} + ) } diff --git a/packages/blog/components/Header/Hero/Hero.stories.tsx b/packages/blog/components/Header/Hero/Hero.stories.tsx new file mode 100644 index 0000000..f3d4947 --- /dev/null +++ b/packages/blog/components/Header/Hero/Hero.stories.tsx @@ -0,0 +1,65 @@ +import { ComponentMeta, ComponentStory } from '@storybook/react' + +import Hero from './Hero' + +export default { + title: 'Components/Header/Hero', + component: Hero, +} as ComponentMeta + +const Template: ComponentStory = (args) => + +export const Default = Template.bind({}) +Default.args = { + title: 'Hello World', + subtitle: 'This is a subtitle', + image: { + src: 'https://images.unsplash.com/photo-1497316730643-415fac54a2af?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=928&q=80', + alt: 'Photo by Michael Henry', + }, +} + +export const Mobile1 = Template.bind({}) +Mobile1.args = { + title: 'Hello World', + subtitle: 'This is a subtitle', + image: { + src: 'https://images.unsplash.com/photo-1497316730643-415fac54a2af?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=928&q=80', + alt: 'Photo by Michael Henry', + }, +} +Mobile1.parameters = { + viewport: { + defaultViewport: 'mobile1', + }, +} + +export const Mobile2 = Template.bind({}) +Mobile2.args = { + title: 'Hello World', + subtitle: 'This is a subtitle', + image: { + src: 'https://images.unsplash.com/photo-1497316730643-415fac54a2af?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=928&q=80', + alt: 'Photo by Michael Henry', + }, +} +Mobile2.parameters = { + viewport: { + defaultViewport: 'mobile2', + }, +} + +export const Tablet = Template.bind({}) +Tablet.args = { + title: 'Hello World', + subtitle: 'This is a subtitle', + image: { + src: 'https://images.unsplash.com/photo-1497316730643-415fac54a2af?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=928&q=80', + alt: 'Photo by Michael Henry', + }, +} +Tablet.parameters = { + viewport: { + defaultViewport: 'tablet', + }, +} diff --git a/packages/blog/components/Header/Hero/Hero.styled.tsx b/packages/blog/components/Header/Hero/Hero.styled.tsx new file mode 100644 index 0000000..b2f2cda --- /dev/null +++ b/packages/blog/components/Header/Hero/Hero.styled.tsx @@ -0,0 +1,72 @@ +import { motion } from 'framer-motion' +import styled from 'styled-components' +import { breakPoint } from '../../../styles/theme' +import { mediaPadding } from '../Header.styled' + +const Styled = { + container: styled(motion.div)` + width: 100%; + + max-width: ${breakPoint.xl}px; + margin-left: auto; + margin-right: auto; + + ${mediaPadding} + + display: flex; + gap: 24px; + + flex-direction: row; + align-items: flex-start; + @media screen and (max-width: ${breakPoint.md}px) { + flex-direction: column; + align-items: center; + } + `, + image: styled(motion.img)` + width: 180px; + height: 180px; + + @media screen and (max-width: ${breakPoint.lg}px) { + width: 120px; + height: 120px; + } + @media screen and (max-width: ${breakPoint.md}px) { + width: 100px; + height: 100px; + } + + border-radius: 50%; + `, + infoContainer: styled(motion.div)` + display: flex; + flex-direction: column; + gap: 16px; + + margin-top: 8px; + margin-bottom: 8px; + + align-items: flex-start; + @media screen and (max-width: ${breakPoint.md}px) { + align-items: center; + } + `, + title: styled(motion.h1)` + display: flex; + align-items: center; + font-size: 36px; + font-weight: bold; + line-height: 1.2; + + @media screen and (max-width: ${breakPoint.md}px) { + font-size: 24px; + } + `, + subtitle: styled(motion.h2)` + font-size: 16px; + font-weight: normal; + line-height: 1.2; + `, +} + +export default Styled diff --git a/packages/blog/components/Header/Hero/Hero.tsx b/packages/blog/components/Header/Hero/Hero.tsx new file mode 100644 index 0000000..76f4b09 --- /dev/null +++ b/packages/blog/components/Header/Hero/Hero.tsx @@ -0,0 +1,30 @@ +import { motion } from 'framer-motion' +import { ViewportEventHandler } from 'framer-motion/types/motion/features/viewport/types' +import React from 'react' +import Styled from './Hero.styled' + +export type HeroProps = { + title: string + subtitle: string + image: { + src: string + alt: string + } + onViewportEnter: ViewportEventHandler + onViewportLeave: ViewportEventHandler + pathname: string +} + +const Hero: React.FC = ({ image, title, subtitle, onViewportEnter, onViewportLeave, pathname }) => { + return ( + + + + {title} + {subtitle} + + + ) +} + +export default Hero diff --git a/packages/blog/components/Header/Hero/index.tsx b/packages/blog/components/Header/Hero/index.tsx new file mode 100644 index 0000000..fe53bfb --- /dev/null +++ b/packages/blog/components/Header/Hero/index.tsx @@ -0,0 +1,2 @@ +export { default } from './Hero' +export type { HeroProps } from './Hero' diff --git a/packages/blog/pages/index.tsx b/packages/blog/pages/index.tsx index af5b722..75904f0 100644 --- a/packages/blog/pages/index.tsx +++ b/packages/blog/pages/index.tsx @@ -1,6 +1,8 @@ import type { GetStaticPropsContext, InferGetStaticPropsType, NextPage } from 'next' import Link from 'next/link' +import { motion } from 'framer-motion' import { getAllPosts } from '../libs/post-api' +import Header from '../components/Header' export const getStaticProps = (ctx: GetStaticPropsContext) => { const posts = getAllPosts() @@ -19,16 +21,24 @@ export const getStaticProps = (ctx: GetStaticPropsContext) => { const Home: NextPage> = ({ posts }) => { return ( -
-

craft-blog-starter

- {posts.map((post) => ( - - -

{post.title}

-
- - ))} -
+ <> +
+
+

craft-blog-starter

+ {posts.map((post) => ( + + +

{post.title}

+
+ + ))} +
+ ) } diff --git a/packages/blog/pages/posts/[slug].tsx b/packages/blog/pages/posts/[slug].tsx index 425d4a9..ca740b6 100644 --- a/packages/blog/pages/posts/[slug].tsx +++ b/packages/blog/pages/posts/[slug].tsx @@ -1,8 +1,10 @@ import { GetStaticPaths, GetStaticPathsResult, GetStaticProps, NextPage } from 'next' import { CraftPage } from 'react-craft-x' import styled from 'styled-components' +import { motion } from 'framer-motion' import { getAllPosts } from '../../libs/post-api' import { CraftBlogPage } from '../../types/CraftBlogPage' +import Header from '../../components/Header' export const getStaticProps: GetStaticProps = async (ctx) => { const { slug } = ctx.params! @@ -50,7 +52,12 @@ const PostPage: NextPage<{ margin-right: auto; ` - return + return ( + <> +
+ + + ) } export default PostPage diff --git a/packages/blog/styles/theme.ts b/packages/blog/styles/theme.ts index b3f324b..9a67912 100644 --- a/packages/blog/styles/theme.ts +++ b/packages/blog/styles/theme.ts @@ -14,4 +14,12 @@ export const theme = { color, } +export const breakPoint = { + sm: 640, + md: 768, + lg: 1024, + xl: 1280, + xxl: 1536, +} as const + export type Theme = typeof theme