mirror of
https://github.com/acid-info/logos-press-engine.git
synced 2025-02-23 06:38:27 +00:00
feat: implement content block component
This commit is contained in:
parent
87ed14e1b2
commit
39fea87d83
32
src/components/ContentBlock/ContentBlock.Body.tsx
Normal file
32
src/components/ContentBlock/ContentBlock.Body.tsx
Normal file
@ -0,0 +1,32 @@
|
||||
import { Typography } from '@acid-info/lsd-react'
|
||||
import styled from '@emotion/styled'
|
||||
|
||||
type Props = {
|
||||
title: string
|
||||
author: string
|
||||
}
|
||||
|
||||
const ContentBlockBody = ({ title, author }: Props) => {
|
||||
return (
|
||||
<BlockBodyContainer>
|
||||
<Typography variant="body1" component="div" genericFontFamily="serif">
|
||||
{title}
|
||||
</Typography>
|
||||
<Typography
|
||||
variant="body3"
|
||||
component="div"
|
||||
genericFontFamily="sans-serif"
|
||||
>
|
||||
{author}
|
||||
</Typography>
|
||||
</BlockBodyContainer>
|
||||
)
|
||||
}
|
||||
|
||||
const BlockBodyContainer = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
`
|
||||
|
||||
export default ContentBlockBody
|
35
src/components/ContentBlock/ContentBlock.Header.tsx
Normal file
35
src/components/ContentBlock/ContentBlock.Header.tsx
Normal file
@ -0,0 +1,35 @@
|
||||
import styled from '@emotion/styled'
|
||||
import { Typography } from '@acid-info/lsd-react'
|
||||
import { PostClassType } from '../Post/Post'
|
||||
|
||||
type Props = {
|
||||
type: PostClassType
|
||||
date: string
|
||||
}
|
||||
|
||||
const ContentBlockHeader = ({ type, date }: Props) => {
|
||||
return (
|
||||
<ContentBlockInfo>
|
||||
<Typography variant="body3" genericFontFamily="sans-serif">
|
||||
{type.toUpperCase()}
|
||||
</Typography>
|
||||
<Typography variant="body3">•</Typography>
|
||||
<Typography variant="body3" genericFontFamily="sans-serif">
|
||||
{new Date(date).toLocaleString('en-GB', {
|
||||
day: 'numeric',
|
||||
month: 'long',
|
||||
year: 'numeric',
|
||||
})}
|
||||
</Typography>
|
||||
</ContentBlockInfo>
|
||||
)
|
||||
}
|
||||
|
||||
const ContentBlockInfo = styled.div`
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
`
|
||||
|
||||
export default ContentBlockHeader
|
64
src/components/ContentBlock/ImageBlock.tsx
Normal file
64
src/components/ContentBlock/ImageBlock.tsx
Normal file
@ -0,0 +1,64 @@
|
||||
import Link from 'next/link'
|
||||
import styled from '@emotion/styled'
|
||||
import Image from 'next/image'
|
||||
|
||||
import { SearchResultItem } from '@/types/data.types'
|
||||
import { UnbodyImageBlock } from '@/lib/unbody/unbody.types'
|
||||
|
||||
import { GridItem } from '../Grid/Grid'
|
||||
import { PostClassType } from '../Post/Post'
|
||||
import ContentBlockHeader from './ContentBlock.Header'
|
||||
import ContentBlockBody from './ContentBlock.Body'
|
||||
|
||||
type Props = Omit<SearchResultItem<UnbodyImageBlock>, 'score'>
|
||||
|
||||
const ImageBlock = ({ doc }: Props) => {
|
||||
return (
|
||||
<CustomGridItem className="w-2">
|
||||
<BlockLink href={`/article/${doc.document[0].slug}`}>
|
||||
<Container>
|
||||
<ImageContainer>
|
||||
<Image fill src={doc.url} alt={doc.alt} />
|
||||
</ImageContainer>
|
||||
<ContentBlockHeader
|
||||
type={PostClassType.ARTICLE}
|
||||
// TODO: type error with GoogleCalendarEvent
|
||||
// which one do we use for date between createdAt and modifiedAt?
|
||||
date={doc?.document[0].modifiedAt}
|
||||
/>
|
||||
<ContentBlockBody
|
||||
title={doc.document[0].title}
|
||||
author="Jason Freeman"
|
||||
/>
|
||||
</Container>
|
||||
</BlockLink>
|
||||
</CustomGridItem>
|
||||
)
|
||||
}
|
||||
|
||||
const CustomGridItem = styled(GridItem)`
|
||||
@media (max-width: 768px) {
|
||||
grid-column: span 8 !important;
|
||||
}
|
||||
`
|
||||
|
||||
const BlockLink = styled(Link)`
|
||||
text-decoration: none;
|
||||
`
|
||||
|
||||
const Container = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
padding: 16px 0;
|
||||
border-top: 1px solid rgb(var(--lsd-theme-primary));
|
||||
position: relative;
|
||||
`
|
||||
|
||||
const ImageContainer = styled.div`
|
||||
position: relative;
|
||||
margin-bottom: 8px;
|
||||
aspect-ratio: 1 / 1; // fixed aspect ratio temporarily
|
||||
`
|
||||
|
||||
export default ImageBlock
|
49
src/components/ContentBlock/TextBlock.tsx
Normal file
49
src/components/ContentBlock/TextBlock.tsx
Normal file
@ -0,0 +1,49 @@
|
||||
import Link from 'next/link'
|
||||
import styled from '@emotion/styled'
|
||||
|
||||
import { SearchResultItem } from '@/types/data.types'
|
||||
import { UnbodyTextBlock } from '@/lib/unbody/unbody.types'
|
||||
|
||||
import { GridItem } from '../Grid/Grid'
|
||||
import { Typography } from '@acid-info/lsd-react'
|
||||
import { PostClassType } from '../Post/Post'
|
||||
import ContentBlockHeader from './ContentBlock.Header'
|
||||
import ContentBlockBody from './ContentBlock.Body'
|
||||
|
||||
type Props = Omit<SearchResultItem<UnbodyTextBlock>, 'score'>
|
||||
|
||||
const TextBlock = ({ doc }: Props) => {
|
||||
return (
|
||||
<GridItem className="w-4">
|
||||
<BlockLink href={`/article/${doc.document[0].slug}`}>
|
||||
<Container>
|
||||
<ContentBlockHeader
|
||||
type={PostClassType.ARTICLE}
|
||||
date={doc?.document[0].modifiedAt}
|
||||
/>
|
||||
<Typography variant="body2" genericFontFamily="sans-serif">
|
||||
{doc.text}
|
||||
</Typography>
|
||||
<ContentBlockBody
|
||||
title={doc.document[0].title}
|
||||
author="Jason Freeman"
|
||||
/>
|
||||
</Container>
|
||||
</BlockLink>
|
||||
</GridItem>
|
||||
)
|
||||
}
|
||||
|
||||
const BlockLink = styled(Link)`
|
||||
text-decoration: none;
|
||||
`
|
||||
|
||||
const Container = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
padding: 16px 0;
|
||||
border-top: 1px solid rgb(var(--lsd-theme-primary));
|
||||
`
|
||||
|
||||
export default TextBlock
|
2
src/components/ContentBlock/index.ts
Normal file
2
src/components/ContentBlock/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export { default as ImageBlock } from './ImageBlock'
|
||||
export { default as TextBlock } from './TextBlock'
|
@ -6,9 +6,10 @@ export const Grid = styled.div`
|
||||
padding: 16px;
|
||||
gap: 16px;
|
||||
|
||||
@media (max-width: 768px) {
|
||||
// TODO: The mobile design works when commenting this out
|
||||
/* @media (max-width: 768px) {
|
||||
grid-template-columns: 100%;
|
||||
}
|
||||
} */
|
||||
`
|
||||
|
||||
export const GridItem = styled.div`
|
||||
|
41
src/components/RelatedArticles/RelatedArticles.tsx
Normal file
41
src/components/RelatedArticles/RelatedArticles.tsx
Normal file
@ -0,0 +1,41 @@
|
||||
import { getArticleCover } from '@/utils/data.utils'
|
||||
import { Typography } from '@acid-info/lsd-react'
|
||||
import styled from '@emotion/styled'
|
||||
import { PostsList } from '../PostList/PostList'
|
||||
import { Section } from '../Section/Section'
|
||||
import { SearchResultItem } from '@/types/data.types'
|
||||
import { UnbodyGoogleDoc } from '@/lib/unbody/unbody.types'
|
||||
|
||||
type Props = {
|
||||
articles: SearchResultItem<UnbodyGoogleDoc>[]
|
||||
}
|
||||
|
||||
export default function RelatedArticles({ articles }: Props) {
|
||||
return (
|
||||
<Container>
|
||||
<Section title={'Related Articles'} matches={articles?.length}>
|
||||
<PostsList
|
||||
posts={articles.map((article) => ({
|
||||
slug: article.doc.slug,
|
||||
date: article.doc.modifiedAt,
|
||||
title: article.doc.title,
|
||||
description: article.doc.subtitle, // TODO: summary is not available
|
||||
author: 'Jinho',
|
||||
tags: article.doc.tags,
|
||||
coverImage: getArticleCover(article.doc.blocks),
|
||||
}))}
|
||||
/>
|
||||
</Section>
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
|
||||
const Container = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
||||
@media (max-width: 768px) {
|
||||
align-items: flex-start;
|
||||
}
|
||||
`
|
1
src/components/RelatedArticles/index.tsx
Normal file
1
src/components/RelatedArticles/index.tsx
Normal file
@ -0,0 +1 @@
|
||||
export { default as RelatedArticles } from './RelatedArticles'
|
51
src/components/RelatedContent/RelatedContent.tsx
Normal file
51
src/components/RelatedContent/RelatedContent.tsx
Normal file
@ -0,0 +1,51 @@
|
||||
import styled from '@emotion/styled'
|
||||
import { Section } from '../Section/Section'
|
||||
import { SearchResultItem } from '@/types/data.types'
|
||||
import { UnbodyImageBlock, UnbodyTextBlock } from '@/lib/unbody/unbody.types'
|
||||
import { Grid } from '../Grid/Grid'
|
||||
import { ImageBlock, TextBlock } from '../ContentBlock'
|
||||
import { UnbodyGraphQl } from '@/lib/unbody/unbody-content.types'
|
||||
|
||||
type Props = {
|
||||
blocks: SearchResultItem<UnbodyImageBlock | UnbodyTextBlock>[]
|
||||
}
|
||||
|
||||
export default function RelatedContent({ blocks }: Props) {
|
||||
return (
|
||||
<Container>
|
||||
<Section title={'Related Content'} matches={blocks?.length}>
|
||||
<Grid>
|
||||
{blocks.map(
|
||||
(block: SearchResultItem<UnbodyImageBlock | UnbodyTextBlock>) => {
|
||||
if (!block.doc.document || !block.doc.document[0]) return null
|
||||
|
||||
let refArticle = null
|
||||
if (UnbodyGraphQl.UnbodyDocumentTypeNames.GoogleDoc) {
|
||||
refArticle = block.doc.document[0]
|
||||
}
|
||||
|
||||
switch (block.doc.__typename) {
|
||||
case UnbodyGraphQl.UnbodyDocumentTypeNames.TextBlock:
|
||||
return <TextBlock doc={block.doc} />
|
||||
case UnbodyGraphQl.UnbodyDocumentTypeNames.ImageBlock: {
|
||||
return <ImageBlock doc={block.doc} />
|
||||
}
|
||||
}
|
||||
},
|
||||
)}
|
||||
</Grid>
|
||||
</Section>
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
|
||||
const Container = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
margin-top: 108px;
|
||||
|
||||
@media (max-width: 768px) {
|
||||
align-items: flex-start;
|
||||
}
|
||||
`
|
1
src/components/RelatedContent/index.tsx
Normal file
1
src/components/RelatedContent/index.tsx
Normal file
@ -0,0 +1 @@
|
||||
export { default as RelatedContent } from './RelatedContent'
|
@ -4,30 +4,18 @@ import {
|
||||
UnbodyImageBlock,
|
||||
UnbodyTextBlock,
|
||||
} from '@/lib/unbody/unbody.types'
|
||||
import { UnbodyGraphQl } from '@/lib/unbody/unbody-content.types'
|
||||
|
||||
import Image from 'next/image'
|
||||
|
||||
import unbodyApi from '@/services/unbody.service'
|
||||
import { PostTypes, SearchResultItem } from '@/types/data.types'
|
||||
import {
|
||||
PostTypes,
|
||||
SearchHook,
|
||||
SearchHookDataPayload,
|
||||
SearchResultItem,
|
||||
SearchResults,
|
||||
} from '@/types/data.types'
|
||||
import {
|
||||
createMinimizedSearchText,
|
||||
extractQueryFromQuery,
|
||||
extractTopicsFromQuery,
|
||||
} from '@/utils/search.utils'
|
||||
import { useRouter } from 'next/router'
|
||||
import { ReactNode, useEffect, useRef, useState } from 'react'
|
||||
import Link from 'next/link'
|
||||
import { SearchLayout } from '@/layouts/SearchLayout'
|
||||
import { Section } from '@/components/Section/Section'
|
||||
import { PostsList } from '@/components/PostList/PostList'
|
||||
import { getArticleCover } from '@/utils/data.utils'
|
||||
import { RelatedArticles } from '@/components/RelatedArticles'
|
||||
import { RelatedContent } from '@/components/RelatedContent'
|
||||
|
||||
interface SearchPageProps {
|
||||
articles: SearchResultItem<UnbodyGoogleDoc>[]
|
||||
@ -83,83 +71,10 @@ export default function SearchPage({
|
||||
|
||||
return (
|
||||
<div>
|
||||
{articles.data?.length && (
|
||||
<Section title={'Related Articles'} matches={articles.data?.length}>
|
||||
<PostsList
|
||||
posts={articles.data.map((article) => ({
|
||||
slug: article.doc.slug,
|
||||
date: article.doc.modifiedAt,
|
||||
title: article.doc.title,
|
||||
description: article.doc.subtitle, // TODO: summary is not available
|
||||
author: 'Jinho',
|
||||
tags: article.doc.tags,
|
||||
coverImage: getArticleCover(article.doc.blocks),
|
||||
}))}
|
||||
/>
|
||||
</Section>
|
||||
)}
|
||||
{articles.data?.length && <RelatedArticles articles={articles.data} />}
|
||||
|
||||
<section>
|
||||
<strong>Related content blocks</strong>
|
||||
<hr />
|
||||
<div>
|
||||
{blocks.loading && <div>...</div>}
|
||||
{!blocks.error && blocks.data && blocks.data.length > 0 ? (
|
||||
blocks.data.map(
|
||||
(block: SearchResultItem<UnbodyImageBlock | UnbodyTextBlock>) => {
|
||||
if (!block.doc.document || !block.doc.document[0]) return null
|
||||
|
||||
let refArticle = null
|
||||
if (UnbodyGraphQl.UnbodyDocumentTypeNames.GoogleDoc) {
|
||||
refArticle = block.doc.document[0]
|
||||
}
|
||||
|
||||
switch (block.doc.__typename) {
|
||||
case UnbodyGraphQl.UnbodyDocumentTypeNames.TextBlock:
|
||||
return (
|
||||
<div key={block.doc.slug}>
|
||||
{refArticle && (
|
||||
<h3>
|
||||
<Link href={`/articles/${refArticle.slug}`}>
|
||||
{refArticle.title}
|
||||
</Link>
|
||||
</h3>
|
||||
)}
|
||||
{
|
||||
<p
|
||||
dangerouslySetInnerHTML={{ __html: block.doc.html }}
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
)
|
||||
case UnbodyGraphQl.UnbodyDocumentTypeNames.ImageBlock: {
|
||||
return (
|
||||
<div key={block.doc.slug}>
|
||||
{refArticle && (
|
||||
<h3>
|
||||
<Link href={`/articles/${refArticle.slug}`}>
|
||||
{refArticle.title}
|
||||
</Link>
|
||||
</h3>
|
||||
)}
|
||||
<Image
|
||||
title={block.doc.alt}
|
||||
src={block.doc.url}
|
||||
width={block.doc.width}
|
||||
height={block.doc.height}
|
||||
alt={block.doc.alt}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
) : (
|
||||
<div>Nothing found</div>
|
||||
)}
|
||||
</div>
|
||||
</section>
|
||||
{/* TODO: used initialBlocks instead of blocks.data temporarily to render data */}
|
||||
{initialBlocks?.length && <RelatedContent blocks={initialBlocks} />}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ export const getSearchBlocksQuery = (args: UnbodyGetFilters = defaultArgs) =>
|
||||
title
|
||||
mentions
|
||||
slug
|
||||
modifiedAt
|
||||
__typename
|
||||
}
|
||||
}
|
||||
@ -39,6 +40,7 @@ export const getSearchBlocksQuery = (args: UnbodyGetFilters = defaultArgs) =>
|
||||
title
|
||||
slug
|
||||
mentions
|
||||
modifiedAt
|
||||
__typename
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user