Merge pull request #85 from acid-info/postcard

add postcard based on new design
This commit is contained in:
jeangovil 2023-08-17 17:43:03 +03:30 committed by GitHub
commit 734c548022
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 245 additions and 373 deletions

View File

@ -10,7 +10,7 @@ import { Typography } from '@acid-info/lsd-react'
import styled from '@emotion/styled'
import ReactPlayer from 'react-player'
import { LPE } from '../../types/lpe.types'
import { PostImageRatio } from '../Post/Post'
import { PostImageRatio } from '@/components/PostCard/PostCard'
import { ArticleImageBlockWrapper } from './Article.ImageBlockWrapper'
export const RenderArticleBlock = ({

View File

@ -1,7 +1,10 @@
import styled from '@emotion/styled'
import Image from 'next/image'
import { LPE } from '../../types/lpe.types'
import { PostImageRatio, PostImageRatioOptions } from '../Post/Post'
import {
PostImageRatio,
PostImageRatioOptions,
} from '@/components/PostCard/PostCard'
import { ResponsiveImage } from '../ResponsiveImage/ResponsiveImage'
type Props = {

View File

@ -6,7 +6,7 @@ import { useIntersectionObserver } from '@/utils/ui.utils'
import { Typography } from '@acid-info/lsd-react'
import styled from '@emotion/styled'
import { LPE } from '../../../types/lpe.types'
import { PostImageRatio } from '../../Post/Post'
import { PostImageRatio } from '@/components/PostCard/PostCard'
import { ArticleImageBlockWrapper } from '../Article.ImageBlockWrapper'
import ArticleStats from '../Article.Stats'
import ArticleSummary from './Article.Summary'

View File

@ -1,91 +0,0 @@
import styled from '@emotion/styled'
import { LPE } from '../../types/lpe.types'
import { Grid, GridItem } from '../Grid/Grid'
import Post, { PostSize } from '../Post/Post'
type Props = {
post: LPE.Article.Data
}
const FeaturedPost = ({ post }: Props) => {
return (
<CustomGrid>
<GridItem className="w-16">
<PostWrapper>
<Post
data={{
authors: post.authors,
date: post.modifiedAt ? new Date(post.modifiedAt) : null,
slug: post.slug,
title: post.title,
coverImage: post.coverImage,
description: post.subtitle,
summary: post.summary,
tags: post.tags,
}}
appearance={{
size: PostSize.LARGE,
imagePropsArray: [
{
fill: true,
height: '432px',
className: 'desktop',
nextImageProps: post.coverImage
? {
quality: 100,
width: post.coverImage?.width * 2,
height: post.coverImage?.height * 2,
}
: {},
},
{
fill: false,
className: 'mobile',
nextImageProps: post.coverImage
? {
quality: 100,
width: post.coverImage?.width * 2,
height: post.coverImage?.height * 2,
}
: {},
},
],
}}
isFeatured
/>
</PostWrapper>
</GridItem>
</CustomGrid>
)
}
const CustomGrid = styled(Grid)`
margin-bottom: 108px;
`
const PostWrapper = styled.div`
margin-top: 16px;
padding: 16px 0;
border-top: 1px solid rgb(var(--lsd-theme-primary));
width: 100%;
.mobile {
display: none;
}
.desktop {
display: block;
}
@media (max-width: 768px) {
.desktop {
display: none;
}
.mobile {
display: block;
}
}
`
export default FeaturedPost

View File

@ -1 +0,0 @@
export { default as FeaturedPost } from './FeaturedPost'

View File

@ -0,0 +1,12 @@
import { FC } from 'react'
interface Props {
post: any
}
// TODO: this needs to be removed and replaced by Postcard component
export const FeaturedPost: FC<Props> = ({ post }) => {
return null
}
export default FeaturedPost

View File

@ -1,272 +0,0 @@
import { Tags } from '@/components/Tags'
import { Typography } from '@acid-info/lsd-react'
import { CommonProps } from '@acid-info/lsd-react/dist/utils/useCommonProps'
import styled from '@emotion/styled'
import Link from 'next/link'
import React, { useMemo } from 'react'
import { LPE } from '../../types/lpe.types'
import { Authors } from '../Authors'
import { AuthorsDirection } from '../Authors/Authors'
import { LogosCircleIcon } from '../Icons/LogosCircleIcon'
import {
ResponsiveImage,
ResponsiveImageProps,
} from '../ResponsiveImage/ResponsiveImage'
export enum PostImageRatio {
PORTRAIT = 'portrait',
LANDSCAPE = 'landscape',
SQUARE = 'square',
}
export enum PostClassType {
ARTICLE = 'article',
PODCAST = 'podcast',
}
export enum PostStyleType {
LSD = 'lsd',
DEFAULT = 'default',
}
export enum PostSize {
SMALL = 'small',
LARGE = 'large',
}
export enum PostType {
BODY = 'body',
HEADER = 'header',
}
export type PostAppearanceProps = {
size?: PostSize
classType?: PostClassType
postType?: PostType
styleType?: PostStyleType
aspectRatio?: PostImageRatio
showImage?: boolean
imageProps?: ResponsiveImageProps
imagePropsArray?: ResponsiveImageProps[]
}
export type PostDataProps = {
slug: string
date: Date | null
title: string
description?: string
authors: LPE.Author.Document[]
tags?: string[]
coverImage?: LPE.Article.Data['coverImage'] | null
summary?: string
}
export const PostImageRatioOptions = {
[PostImageRatio.PORTRAIT]: '9 / 16',
[PostImageRatio.LANDSCAPE]: '16 / 9',
[PostImageRatio.SQUARE]: '1 / 1',
}
export type PostProps = CommonProps &
React.HTMLAttributes<HTMLDivElement> & {
appearance?: PostAppearanceProps
data: PostDataProps
isFeatured?: boolean
}
export default function Post({
appearance: {
size = PostSize.SMALL,
classType = PostClassType.ARTICLE,
postType = PostType.BODY,
showImage = true,
imageProps,
imagePropsArray = [],
} = {},
data: {
coverImage = null,
date,
title,
description,
authors,
slug,
tags = [],
},
isFeatured = false,
...props
}: PostProps) {
const _title = useMemo(
() => (
<TitleLink href={`/article/${slug}`}>
<Title
variant={size === PostSize.SMALL ? 'h4' : 'h1'}
genericFontFamily="serif"
component="h2"
>
{title}
</Title>
</TitleLink>
),
[title, size, slug],
)
const _description = useMemo(
() =>
classType == PostClassType.ARTICLE && (
<Description
variant={size === PostSize.SMALL ? 'body2' : 'h6'}
genericFontFamily="sans-serif"
isFeatured={isFeatured}
>
{description}
</Description>
),
[classType, description, isFeatured, size],
)
const _thumbnail = useMemo(() => {
if (!showImage || !coverImage) return null
let allImageProps = [
...imagePropsArray,
...(imageProps ? [imageProps] : []),
]
if (postType === PostType.BODY) {
return (
<Link href={`/article/${slug}`}>
{allImageProps.length > 0 ? (
allImageProps.map((_imageProps, index) => (
<ResponsiveImage key={index} {..._imageProps} data={coverImage} />
))
) : (
<ResponsiveImage {...imageProps} data={coverImage} />
)}
</Link>
)
} else {
return (
<>
<Link href={`/article/${slug}`}>
{allImageProps.length > 0 ? (
allImageProps.map((_imageProps, index) => (
<ResponsiveImage
key={index}
{..._imageProps}
data={coverImage}
/>
))
) : (
<ResponsiveImage {...imageProps} data={coverImage} />
)}
</Link>
{_title}
{_description}
</>
)
}
}, [slug, showImage, coverImage, postType, imageProps, _title, _description])
const _header = useMemo(() => {
if (postType === 'body')
return (
<>
<HeaderContainer isFeatured={isFeatured}>
<Row>
<Typography variant="body3" genericFontFamily="sans-serif">
{classType.toUpperCase()}
</Typography>
<Typography variant="body3"></Typography>
<Typography variant="body3" genericFontFamily="sans-serif">
{date &&
date.toLocaleString('en-GB', {
day: 'numeric',
month: 'long', // TODO: Should be uppercase
year: 'numeric',
})}
</Typography>
</Row>
{_title}
</HeaderContainer>
</>
)
}, [postType, classType, isFeatured, date, _title])
return (
<Container {...props}>
{_thumbnail}
{_header}
{postType === 'body' && _description}
{classType === 'article' ? (
<Authors
authors={authors}
email={false}
flexDirection={AuthorsDirection.ROW}
gap={8}
/>
) : (
<PodcastAuthor>
<LogosCircleIcon color="primary" />
<Typography variant="body3" genericFontFamily="sans-serif">
Network State
</Typography>
</PodcastAuthor>
)}
{tags.length > 0 && <Tags tags={tags} />}
</Container>
)
}
const Container = styled.div`
display: flex;
flex-direction: column;
position: 'relative';
gap: 16px;
`
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 PodcastAuthor = styled.div`
display: flex;
align-items: center;
gap: 12px;
`
const TitleLink = styled(Link)`
text-decoration: none;
width: fit-content;
`
const HeaderContainer = styled(CustomTypography)<{ isFeatured: boolean }>`
@media (min-width: 768px) {
margin-right: ${({ isFeatured }) => (isFeatured ? '178px' : '0px')};
}
`
const Description = styled(CustomTypography)<{ isFeatured: boolean }>`
@media (min-width: 768px) {
margin-right: ${({ isFeatured }) => (isFeatured ? '178px' : '0px')};
}
@media (max-width: 768px) {
font-size: 14px;
line-height: 20px;
}
`
const Title = styled(CustomTypography)`
@media (max-width: 768px) {
font-size: 28px;
line-height: 36px;
}
`

View File

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

View File

@ -0,0 +1,26 @@
import React, { FC } from 'react'
import {
ResponsiveImage,
ResponsiveImageProps,
} from '@/components/ResponsiveImage/ResponsiveImage'
import { LPE } from '@/types/lpe.types'
import Link from 'next/link'
interface Props {
imageProps: ResponsiveImageProps
imageData: LPE.Image.Document
playIcon?: boolean
link: string
}
export const PostCardCover: FC<Props> = ({
link,
imageProps,
imageData,
playIcon,
}) => {
return (
<Link href={link}>
<ResponsiveImage {...imageProps} data={imageData} />
</Link>
)
}

View File

@ -0,0 +1,39 @@
import { Typography } from '@acid-info/lsd-react'
import React, { FC } from 'react'
import styled from '@emotion/styled'
import { LPE } from '@/types/lpe.types'
import PostType = LPE.PostType
interface Props {
contentType: PostType
date: Date | null
}
export const PostCardLabel: FC<Props> = ({ contentType, date }) => {
return (
<Container>
<Typography variant="body3" genericFontFamily="sans-serif">
{contentType.toUpperCase()}
</Typography>
<Typography variant="body3"></Typography>
<Typography variant="body3" genericFontFamily="sans-serif">
{date &&
date.toLocaleString('en-GB', {
day: 'numeric',
month: 'long', // TODO: Should be uppercase
year: 'numeric',
})}
</Typography>
</Container>
)
}
const Container = styled.div`
display: flex;
flex-direction: row;
align-items: center;
gap: 8px;
margin-bottom: 8px;
`

View File

@ -0,0 +1,18 @@
import { LPE } from '@/types/lpe.types'
import Link from 'next/link'
export interface PostCardShowDetailsProps {
title: string
slug: string
episodeNumber: number
logo: LPE.Image.Document
}
// TODO
export const PostCardShowDetails = (props: PostCardShowDetailsProps) => {
return (
<Link href={`/podcast/${props.slug}`}>
<span>{props.title}</span>
</Link>
)
}

View File

@ -0,0 +1,137 @@
import { Tags } from '@/components/Tags'
import { Typography } from '@acid-info/lsd-react'
import { CommonProps } from '@acid-info/lsd-react/dist/utils/useCommonProps'
import styled from '@emotion/styled'
import Link from 'next/link'
import React from 'react'
import { LPE } from '../../types/lpe.types'
import { Authors } from '../Authors'
import { AuthorsDirection } from '../Authors/Authors'
import { ResponsiveImageProps } from '../ResponsiveImage/ResponsiveImage'
import { PostCardLabel } from './PostCard.Label'
import { PostCardCover } from '@/components/PostCard/PostCard.Cover'
import {
PostCardShowDetails,
PostCardShowDetailsProps,
} from '@/components/PostCard/PostCard.ShowDetails'
export type PostAppearanceProps = {
imageProps?: ResponsiveImageProps
}
export type PostDataProps = {
slug: string
date: Date | null
title: string
subtitle?: string
authors?: LPE.Author.Document[]
tags?: string[]
coverImage?: LPE.Article.Data['coverImage'] | null
podcastShowDetails?: PostCardShowDetailsProps
}
export type PostCardProps = CommonProps &
React.HTMLAttributes<HTMLDivElement> & {
appearance?: PostAppearanceProps
data: PostDataProps
isFeatured?: boolean
contentType: LPE.PostType
}
export const PostCard = (_props: PostCardProps) => {
const {
appearance: { imageProps = {} } = {},
data: {
coverImage = null,
date,
title,
subtitle,
authors,
slug,
tags = [],
podcastShowDetails,
},
contentType,
isFeatured = false,
...props
} = _props
return (
<Container {...props}>
{coverImage && (
<PostCardCover
imageProps={imageProps}
imageData={coverImage}
link={`/article/${slug}`}
/>
)}
<PostCardLabel contentType={contentType} date={date} />
<TitleLink href={`/article/${slug}`}>
<Title genericFontFamily="serif" component="h3">
{title}
</Title>
</TitleLink>
{subtitle && (
<Subtitle variant={'body1'} genericFontFamily="sans-serif">
{subtitle}
</Subtitle>
)}
{authors && authors.length > 0 && (
<Authors
authors={authors}
email={false}
flexDirection={AuthorsDirection.ROW}
gap={8}
/>
)}
{podcastShowDetails && <PostCardShowDetails {...podcastShowDetails} />}
{tags.length > 0 && <Tags tags={tags} />}
</Container>
)
}
const Container = styled.div`
display: flex;
flex-direction: column;
position: 'relative';
gap: 16px;
`
const CustomTypography = styled(Typography)`
text-overflow: ellipsis;
overflow: hidden;
word-break: break-word;
`
const PodcastAuthor = styled.div`
display: flex;
align-items: center;
gap: 12px;
`
const TitleLink = styled(Link)`
text-decoration: none;
width: fit-content;
`
const Subtitle = styled(CustomTypography)`
@media (min-width: 768px) {
}
@media (max-width: 768px) {
font-size: 14px;
line-height: 20px;
}
`
const Title = styled(CustomTypography)`
@media (max-width: 768px) {
font-size: 28px;
line-height: 36px;
}
`

View File

@ -0,0 +1,2 @@
export type { PostCardProps } from './PostCard'
export { PostCard } from './PostCard'

View File

@ -4,7 +4,8 @@ import styled from '@emotion/styled'
import { useEffect, useState } from 'react'
import { LPE } from '../../types/lpe.types'
import { Grid, GridItem } from '../Grid/Grid'
import Post from '../Post/Post'
import { PostCard } from '@/components/PostCard'
import PostTypes = LPE.PostTypes
type Props = {
posts: LPE.Article.Data[]
@ -57,17 +58,17 @@ export const PostsList = (props: Props) => {
key={index}
>
<PostWrapper className={props.loading ? 'loading' : ''}>
<Post
<PostCard
data={{
authors: post.authors,
date: post.modifiedAt ? new Date(post.modifiedAt) : null,
slug: post.slug,
title: post.title,
description: post.subtitle,
subtitle: post.subtitle,
coverImage: post.coverImage,
summary: post.summary,
tags: post.tags,
}}
contentType={PostTypes.Article}
/>
</PostWrapper>
</GridItem>