From be6436e9063ef774d11d12ae7ec3f9073ad56ff6 Mon Sep 17 00:00:00 2001 From: jinhojang6 Date: Fri, 21 Apr 2023 01:09:11 +0900 Subject: [PATCH] feat: implement post component --- next.config.js | 5 + .../Icons/LogosCircleIcon/LogosCircleIcon.tsx | 33 +++ src/components/Icons/LogosCircleIcon/index.ts | 1 + src/components/Post/Post.tsx | 193 ++++++++++++++++++ src/components/Post/PostsDemo.tsx | 88 ++++++++ src/components/Post/index.ts | 2 + src/pages/index.tsx | 8 +- 7 files changed, 327 insertions(+), 3 deletions(-) create mode 100644 src/components/Icons/LogosCircleIcon/LogosCircleIcon.tsx create mode 100644 src/components/Icons/LogosCircleIcon/index.ts create mode 100644 src/components/Post/Post.tsx create mode 100644 src/components/Post/PostsDemo.tsx create mode 100644 src/components/Post/index.ts diff --git a/next.config.js b/next.config.js index a843cbe..02a40b6 100644 --- a/next.config.js +++ b/next.config.js @@ -1,6 +1,11 @@ /** @type {import('next').NextConfig} */ const nextConfig = { reactStrictMode: true, + images: { + domains: [ + 'images.pexels.com', + ], + }, } module.exports = nextConfig diff --git a/src/components/Icons/LogosCircleIcon/LogosCircleIcon.tsx b/src/components/Icons/LogosCircleIcon/LogosCircleIcon.tsx new file mode 100644 index 0000000..aa719c5 --- /dev/null +++ b/src/components/Icons/LogosCircleIcon/LogosCircleIcon.tsx @@ -0,0 +1,33 @@ +import { LsdIcon } from '@acid-info/lsd-react' + +export const LogosCircleIcon = LsdIcon( + (props) => ( + + + + + + + ), + { filled: true }, +) diff --git a/src/components/Icons/LogosCircleIcon/index.ts b/src/components/Icons/LogosCircleIcon/index.ts new file mode 100644 index 0000000..b13d0ad --- /dev/null +++ b/src/components/Icons/LogosCircleIcon/index.ts @@ -0,0 +1 @@ +export * from './LogosCircleIcon' diff --git a/src/components/Post/Post.tsx b/src/components/Post/Post.tsx new file mode 100644 index 0000000..5ec3cc6 --- /dev/null +++ b/src/components/Post/Post.tsx @@ -0,0 +1,193 @@ +import { Tag, Typography } from '@acid-info/lsd-react' +import { CommonProps } from '@acid-info/lsd-react/dist/utils/useCommonProps' +import styled from '@emotion/styled' +import Image from 'next/image' +import { LogosCircleIcon } from '../Icons/LogosCircleIcon' +import { useMemo } from 'react' + +export type PostProps = CommonProps & + React.HTMLAttributes & { + size?: 'small' | 'large' + classType?: 'article' | 'podcast' + postType?: 'body' | 'header' + styleType?: 'lsd' | 'default' + aspectRatio?: 'portrait' | 'landscape' | 'square' + showImage?: boolean + imageUrl?: string + date: Date + title: string + description?: string + author?: string + tags?: string[] + } + +const getAspectRatio = (aspectRatio: PostProps['aspectRatio']) => { + switch (aspectRatio) { + case 'portrait': + return '9 / 16' + case 'landscape': + return '16 / 9' + case 'square': + return '1 / 1' + default: + return '16 / 9' + } +} + +export default function Post({ + size = 'small', + classType = 'article', + postType = 'body', + styleType = 'lsd', + aspectRatio = 'landscape', + showImage = true, + imageUrl, + date, + title, + description, + author, + tags = [], + ...props +}: PostProps) { + const _title = useMemo( + () => ( + + {title} + + ), + [title, size], + ) + + const _description = useMemo( + () => + classType == 'article' && ( + + {description} + + ), + [classType, description, size], + ) + + const _thumbnail = useMemo(() => { + if (!showImage || !imageUrl) return null + if (postType === 'body') { + return ( + + + + ) + } else { + // TBD + return ( + + + {_title} + {_description} + + ) + } + }, [showImage, imageUrl, aspectRatio, postType, _title, _description]) + + const _header = useMemo(() => { + if (postType === 'body') + return ( + <> +
+ + + {classType.toUpperCase()} + + + + {date.toLocaleString('en-GB', { + day: 'numeric', + month: 'long', // TODO: Should be uppercase + year: 'numeric', + })} + + + {_title} +
+ + ) + }, [postType, classType, date, _title]) + + return ( + + {_thumbnail} + {_header} + {postType === 'body' && _description} + {classType === 'article' ? ( + + {author} + + ) : ( + + + + Network State + + + )} + {tags.length > 0 && ( + + {tags.map((tag) => ( + + {tag} + + ))} + + )} + + ) +} + +const Container = styled.div` + display: flex; + flex-direction: column; + position: 'relative'; + gap: 16px; +` + +const ThumbnailContainer = styled.div<{ + aspectRatio: PostProps['aspectRatio'] +}>` + aspect-ratio: ${(p) => + p.aspectRatio ? getAspectRatio(p.aspectRatio) : '16 / 9'}; + position: relative; + width: 100%; + height: 100%; + max-height: 458px; // temporary max-height based on the Figma design's max height +` + +const Thumbnail = styled(Image)` + object-fit: cover; +` + +const Row = styled.div` + display: flex; + flex-direction: row; + align-items: center; + gap: 8px; + margin-bottom: 8px; +` + +const CustomTypography = styled(Typography)` + text-overflow: ellipsis; + overflow: hidden; + word-break: break-word; +` +const TagContainer = styled.div` + display: flex; + gap: 8px; + overflow-x: auto; +` + +const PodcastAuthor = styled.div` + display: flex; + align-items: center; + gap: 12px; +` diff --git a/src/components/Post/PostsDemo.tsx b/src/components/Post/PostsDemo.tsx new file mode 100644 index 0000000..f67cf9d --- /dev/null +++ b/src/components/Post/PostsDemo.tsx @@ -0,0 +1,88 @@ +import Post, { PostProps } from './Post' + +const postsData: PostProps[] = [ + { + size: 'small', // 'small' | 'large' + classType: 'article', // 'article' | 'podcast' + postType: 'body', // 'body' | 'header' => TBD + styleType: 'lsd', // 'lsd' | 'default' => WIP + aspectRatio: 'landscape', // 'portrait' | 'landscape' | 'square' + showImage: true, // true | false + imageUrl: + 'https://images.pexels.com/photos/4429335/pexels-photo-4429335.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2', + date: new Date(), + title: 'Preventing an Orwellian Future with Privacy-Enhancing Technology', + description: + 'We built a pedal-powered generator and controller, which is practical to use as an energy source and exercise machine in a household -- and which you can integrate into a solar PV ', + author: 'Jason Freeman', + tags: ['Privacy', 'Security', 'Liberty'], + }, + { + aspectRatio: 'portrait', // different aspect ratio + imageUrl: + 'https://images.pexels.com/photos/4992820/pexels-photo-4992820.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2', + date: new Date(), + title: 'How to Build a Practical Household Bike Generator', + description: + 'We built a pedal-powered generator and controller, which is practical to use as an energy source and exercise machine in a household -- and which you can integrate into a solar PV', + author: 'Jason Freeman', + tags: ['Privacy', 'Security', 'Liberty'], + }, + { + classType: 'podcast', // podcast + imageUrl: + 'https://images.pexels.com/photos/6039256/pexels-photo-6039256.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2', + date: new Date(), + title: 'How to Build a Practical Household Bike Generator', + author: 'Jason Freeman', + tags: ['Privacy', 'Security', 'Liberty'], + }, + { + showImage: false, // without image + classType: 'article', + date: new Date(), + title: 'How to Build a Practical Household Bike Generator', + description: + 'We built a pedal-powered generator and controller, which is practical to use as an energy source and exercise machine in a household -- and which you can integrate into a solar PV', + author: 'Jason Freeman', + tags: ['Privacy', 'Security', 'Liberty'], + }, + { + postType: 'header', // header? TBD + imageUrl: + 'https://images.pexels.com/photos/6039256/pexels-photo-6039256.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2', + date: new Date(), + title: 'How to Build a Practical Household Bike Generator', + description: + 'We built a pedal-powered generator and controller, which is practical to use as an energy source and exercise machine in a household -- and which you can integrate into a solar PV', + }, +] + +const PostsDemo = () => { + return ( +
+ {/* For Demo purposes only. Use inline CSS and styled components temporarily */} +
+ {postsData.map((post, index) => ( +
+ +
+ ))} +
+
+ ) +} + +export default PostsDemo diff --git a/src/components/Post/index.ts b/src/components/Post/index.ts new file mode 100644 index 0000000..1083f45 --- /dev/null +++ b/src/components/Post/index.ts @@ -0,0 +1,2 @@ +export { default as Post } from './Post' +export { default as PostsDemo } from './PostsDemo' diff --git a/src/pages/index.tsx b/src/pages/index.tsx index 838485b..42cab72 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -1,5 +1,6 @@ -import { HeaderLayout } from "@/layouts/HeaderLayout"; -import Head from "next/head"; +import PostsDemo from '@/components/Post/PostsDemo' +import { HeaderLayout } from '@/layouts/HeaderLayout' +import Head from 'next/head' export default function Home() { return ( @@ -15,7 +16,8 @@ export default function Home() {
+
- ); + ) }