feat: implement post component

This commit is contained in:
jinhojang6 2023-04-21 01:09:11 +09:00
parent 8e88fd14e1
commit be6436e906
7 changed files with 327 additions and 3 deletions

View File

@ -1,6 +1,11 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
images: {
domains: [
'images.pexels.com',
],
},
}
module.exports = nextConfig

View File

@ -0,0 +1,33 @@
import { LsdIcon } from '@acid-info/lsd-react'
export const LogosCircleIcon = LsdIcon(
(props) => (
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<rect
width="24"
height="24"
rx="12"
fill="rgb(var(--lsd-icon-primary))"
/>
<path
d="M16.1203 16.52H8V11.5829H16.1126L16.1203 16.52ZM12.0512 11.979C11.3581 11.9797 10.6759 12.1491 10.0652 12.4721C9.45444 12.7951 8.93406 13.2618 8.55022 13.8307C8.49944 13.8885 8.47149 13.9624 8.47149 14.0388C8.47149 14.1153 8.49944 14.1892 8.55022 14.247C8.93653 14.8132 9.4579 15.2774 10.0686 15.5987C10.6793 15.9201 11.3607 16.0888 12.0529 16.09C12.7452 16.0913 13.4272 15.925 14.039 15.6059C14.6509 15.2868 15.174 14.8245 15.5624 14.2596C15.6178 14.1985 15.6484 14.1195 15.6484 14.0376C15.6484 13.9557 15.6178 13.8766 15.5624 13.8156C15.1751 13.249 14.6521 12.7852 14.0397 12.4653C13.4272 12.1454 12.7443 11.9793 12.0512 11.9815V11.979Z"
fill="rgb(var(--lsd-icon-secondary))"
/>
<path
d="M12.0742 11.1212C11.3856 11.1198 10.7075 10.9539 10.0982 10.6375C9.4889 10.3211 8.96651 9.86384 8.57582 9.30479C8.51068 9.23648 8.47442 9.14626 8.47442 9.05251C8.47442 8.95876 8.51068 8.86854 8.57582 8.80023C8.96629 8.24219 9.48902 7.78652 10.0987 7.47281C10.7083 7.1591 11.3864 6.99682 12.0741 7.00005C12.7617 7.00328 13.4382 7.17193 14.0448 7.49136C14.6514 7.81079 15.1697 8.27133 15.5547 8.83302C15.6099 8.89366 15.6405 8.97228 15.6405 9.05376C15.6405 9.13525 15.6099 9.21386 15.5547 9.27451C15.1702 9.83763 14.6523 10.3 14.0459 10.6218C13.4396 10.9435 12.7628 11.1149 12.0742 11.1212ZM13.5893 9.07773C13.5964 8.77695 13.5124 8.4809 13.3481 8.22737C13.1838 7.97385 12.9465 7.77435 12.6666 7.65435C12.3868 7.53435 12.0769 7.49929 11.7768 7.55364C11.4766 7.608 11.1996 7.74931 10.9813 7.95952C10.763 8.16974 10.6131 8.43933 10.5509 8.73388C10.4887 9.02843 10.5169 9.33456 10.632 9.61322C10.7471 9.89187 10.9438 10.1304 11.1971 10.2983C11.4503 10.4663 11.7486 10.556 12.0538 10.5561C12.4549 10.5562 12.8401 10.4016 13.1271 10.1253C13.414 9.84903 13.5799 9.47303 13.5893 9.07773Z"
fill="rgb(var(--lsd-icon-secondary))"
/>
<path
d="M12.0588 15.5285C11.7612 15.5235 11.4717 15.432 11.2268 15.2653C10.9818 15.0986 10.7923 14.8643 10.682 14.5918C10.5717 14.3193 10.5456 14.0207 10.6069 13.7336C10.6682 13.4465 10.8143 13.1836 11.0267 12.9781C11.2391 12.7725 11.5084 12.6335 11.8007 12.5783C12.093 12.5232 12.3954 12.5545 12.6698 12.6682C12.9441 12.782 13.1783 12.9731 13.3428 13.2176C13.5073 13.4622 13.5948 13.7492 13.5943 14.0426C13.5937 14.2399 13.5533 14.4351 13.4756 14.6169C13.3978 14.7987 13.2842 14.9635 13.1414 15.1017C12.9986 15.2399 12.8293 15.3488 12.6435 15.4221C12.4577 15.4953 12.2589 15.5315 12.0588 15.5285Z"
fill="rgb(var(--lsd-icon-secondary))"
/>
</svg>
),
{ filled: true },
)

View File

@ -0,0 +1 @@
export * from './LogosCircleIcon'

View File

@ -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<HTMLDivElement> & {
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(
() => (
<CustomTypography
variant={size === 'small' ? 'h4' : 'h2'}
genericFontFamily="serif"
>
{title}
</CustomTypography>
),
[title, size],
)
const _description = useMemo(
() =>
classType == 'article' && (
<CustomTypography variant="body3" genericFontFamily="sans-serif">
{description}
</CustomTypography>
),
[classType, description, size],
)
const _thumbnail = useMemo(() => {
if (!showImage || !imageUrl) return null
if (postType === 'body') {
return (
<ThumbnailContainer aspectRatio={aspectRatio}>
<Thumbnail fill src={imageUrl} alt={imageUrl} />
</ThumbnailContainer>
)
} else {
// TBD
return (
<ThumbnailContainer aspectRatio={aspectRatio}>
<Thumbnail fill src={imageUrl} alt={imageUrl} />
{_title}
{_description}
</ThumbnailContainer>
)
}
}, [showImage, imageUrl, aspectRatio, postType, _title, _description])
const _header = useMemo(() => {
if (postType === 'body')
return (
<>
<div>
<Row>
<Typography variant="body3" genericFontFamily="sans-serif">
{classType.toUpperCase()}
</Typography>
<Typography variant="body3"></Typography>
<Typography variant="body3" genericFontFamily="sans-serif">
{date.toLocaleString('en-GB', {
day: 'numeric',
month: 'long', // TODO: Should be uppercase
year: 'numeric',
})}
</Typography>
</Row>
{_title}
</div>
</>
)
}, [postType, classType, date, _title])
return (
<Container {...props}>
{_thumbnail}
{_header}
{postType === 'body' && _description}
{classType === 'article' ? (
<Typography variant="body3" genericFontFamily="sans-serif">
{author}
</Typography>
) : (
<PodcastAuthor>
<LogosCircleIcon color="primary" />
<Typography variant="body3" genericFontFamily="sans-serif">
Network State
</Typography>
</PodcastAuthor>
)}
{tags.length > 0 && (
<TagContainer>
{tags.map((tag) => (
<Tag size="small" disabled={false} key={tag}>
{tag}
</Tag>
))}
</TagContainer>
)}
</Container>
)
}
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;
`

View File

@ -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 (
<div style={{ marginTop: '78px' }}>
{/* For Demo purposes only. Use inline CSS and styled components temporarily */}
<div
style={{
display: 'flex',
flexDirection: 'column',
padding: '16px',
gap: '24px',
}}
>
{postsData.map((post, index) => (
<div
style={{
padding: '16px 0',
borderTop: '1px solid rgb(var(--lsd-theme-primary))',
}}
>
<Post key={index} {...post} />
</div>
))}
</div>
</div>
)
}
export default PostsDemo

View File

@ -0,0 +1,2 @@
export { default as Post } from './Post'
export { default as PostsDemo } from './PostsDemo'

View File

@ -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() {
</Head>
<main>
<HeaderLayout />
<PostsDemo />
</main>
</>
);
)
}