>
+}
+
+export const MobileToc = ({ toc }: Props) => {
+ const { tocIndex, setTocIndex } = useArticleContainerContext()
+
+ return toc?.length > 0 ? (
+
+ {toc.map((toc, idx) => (
+ setTocIndex(idx)}
+ active={idx === tocIndex}
+ variant="body3"
+ key={idx}
+ >
+ {toc.title}
+
+ ))}
+
+ ) : null
+}
+
+const CustomTypography = styled(Typography)`
+ text-overflow: ellipsis;
+ word-break: break-word;
+ white-space: pre-wrap;
+`
+
+const Content = styled(CustomTypography)<{ active: boolean }>`
+ padding: 8px 14px;
+ background-color: ${(p) =>
+ p.active
+ ? 'rgb(var(--lsd-theme-primary))'
+ : 'rgb(var(--lsd-theme-secondary))'};
+ color: ${(p) =>
+ p.active
+ ? 'rgb(var(--lsd-theme-secondary))'
+ : 'rgb(var(--lsd-theme-primary))'};
+`
diff --git a/src/components/Article/Article.Stats.tsx b/src/components/Article/Article.Stats.tsx
new file mode 100644
index 0000000..830036d
--- /dev/null
+++ b/src/components/Article/Article.Stats.tsx
@@ -0,0 +1,36 @@
+import { Typography } from '@acid-info/lsd-react'
+import styled from '@emotion/styled'
+
+const ArticleStats = ({
+ dateStr,
+ readingLength,
+}: {
+ dateStr: string
+ readingLength: number
+}) => (
+
+
+
+ {readingLength} minutes read
+
+ •
+
+ {new Date(dateStr).toLocaleString('en-GB', {
+ day: 'numeric',
+ month: 'long', // TODO: Should be uppercase
+ year: 'numeric',
+ })}
+
+
+
+)
+
+const Row = styled.div`
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ gap: 8px;
+ margin-bottom: 8px;
+`
+
+export default ArticleStats
diff --git a/src/components/Article/Article.module.css b/src/components/Article/Article.module.css
index d0bf48e..3ab7663 100644
--- a/src/components/Article/Article.module.css
+++ b/src/components/Article/Article.module.css
@@ -1,7 +1,3 @@
-.relatedArticles > div > button {
- border-top: none !important;
-}
-
/* temporary breakpoint */
@media (min-width: 1024px) {
.mobileToc {
diff --git a/src/components/Article/ArticleBody.tsx b/src/components/Article/ArticleBody.tsx
deleted file mode 100644
index 88f3929..0000000
--- a/src/components/Article/ArticleBody.tsx
+++ /dev/null
@@ -1,338 +0,0 @@
-import { Quote, Tag, Typography } from '@acid-info/lsd-react'
-import styled from '@emotion/styled'
-import Image from 'next/image'
-import { useMemo } from 'react'
-import { PostImageRatio, PostImageRatioOptions } from '../Post/Post'
-import styles from './Article.module.css'
-import { Collapse } from '@/components/Collapse'
-import { useArticleContainerContext } from '@/containers/ArticleContainer.Context'
-import { ArticleReference } from '../ArticleReference'
-import {
- GoogleDocEnhanced,
- UnbodyGoogleDoc,
- UnbodyImageBlock,
- UnbodyTextBlock,
- UnbodyTocItem,
-} from '@/lib/unbody/unbody.types'
-import { UnbodyGraphQl } from '@/lib/unbody/unbody-content.types'
-
-import { RenderArticleBlock } from './Article.Block'
-import { ArticleImageBlockWrapper } from './Article.ImageBlockWrapper'
-import { getArticleCover, getBodyBlocks } from '@/utils/data.utils'
-import { ArticlePostData } from '@/types/data.types'
-
-interface Props {
- data: ArticlePostData
-}
-
-//@jinho
-//TODO please move everything to a separate file
-const ArticleTags = ({ tags }: { tags: string[] }) =>
- tags.length > 0 ? (
-
- {tags.map((tag) => (
-
- {tag}
-
- ))}
-
- ) : null
-
-const ArticleAuthor = ({
- mention,
-}: {
- mention: UnbodyGraphQl.Fragments.MentionItem
-}) => (
-
-
- {mention.name}
-
-
- {mention.emailAddress}
-
-
-)
-
-const ArticleAuthors = ({
- mentions,
-}: {
- mentions: UnbodyGraphQl.Fragments.MentionItem[]
-}) =>
- mentions.length > 0 ? (
-
- {mentions.map((mention) => (
-
- ))}
-
- ) : null
-
-const ArticleStats = ({
- dateStr,
- readingLength,
-}: {
- dateStr: string
- readingLength: number
-}) => (
-
-
-
- {readingLength} minutes read
-
- •
-
- {new Date(dateStr).toLocaleString('en-GB', {
- day: 'numeric',
- month: 'long', // TODO: Should be uppercase
- year: 'numeric',
- })}
-
-
-
-)
-
-const ArticleSummary = ({ summary }: { summary: string }) => (
- //TODO for ihor to work out the design for this
-
- {summary}
-
-)
-
-const ArticleHeader = ({
- title,
- toc,
- summary,
- subtitle,
- mentions,
- tags,
- modifiedAt,
- blocks,
-}: GoogleDocEnhanced) => {
- const date = new Date(modifiedAt)
-
- const _thumbnail = useMemo(() => {
- const coverImage = getArticleCover(blocks)
- if (!coverImage) return null
- return (
-
- )
- }, [blocks])
-
- return (
-
-
-
- {title}
-
- {subtitle && {subtitle}}
-
-
- {_thumbnail}
-
-
- )
-}
-
-const ArticleFootenotes = ({
- footnotes,
-}: {
- footnotes: UnbodyGraphQl.Fragments.FootnoteItem[]
-}) =>
- footnotes.length > 0 ? (
-
- {footnotes.map((footnote, idx) => (
-
-
-
-
- ))}
-
- ) : null
-
-const RelatedArticles = ({ data }: { data: GoogleDocEnhanced[] }) =>
- data.length > 0 ? (
-
- {data.map((article, idx) => (
-
- ))}
-
- ) : null
-
-const FromSameAuthorsArticles = ({ data }: { data: GoogleDocEnhanced[] }) =>
- data.length > 0 ? (
-
- {data.map((article, idx) => (
-
- ))}
-
- ) : null
-
-const ArticleFooter = ({ data }: { data: ArticlePostData }) => {
- const { article, relatedArticles, articlesFromSameAuthors } = data
-
- const footnotes = useMemo(() => {
- return (
- article.blocks
- // @ts-ignore
- .flatMap((b) =>
- b.__typename === UnbodyGraphQl.UnbodyDocumentTypeNames.TextBlock
- ? b.footnotes
- : [],
- )
- )
- }, [article])
-
- return (
-
- }
- />
-
-
-
- )
-}
-
-const CustomTypography = styled(Typography)`
- text-overflow: ellipsis;
- word-break: break-word;
- white-space: pre-wrap;
-`
-
-const ArticleTitle = styled(CustomTypography)`
- //margin-bottom: 24px;
-`
-
-const ArticleSubtitle = styled(CustomTypography)``
-
-const ArticleSummaryContainer = styled('div')`
- padding-left: 40px;
-`
-
-export default function ArticleBody({ data }: Props) {
- const { title, summary, subtitle, blocks, toc, createdAt, tags, mentions } =
- data.article
- const articleContainer = useArticleContainerContext()
- const { tocIndex, setTocIndex } = articleContainer
-
- const _blocks = useMemo(() => {
- return getBodyBlocks(data.article).map((block, idx) => (
-
- ))
- }, [data.article])
-
- console.log(data)
-
- //TODO
- //@Jinho please move everything (starts with _ ) to a separate file and import it here
- const _mobileToc = useMemo(
- () =>
- toc?.length > 0 && (
-
- {toc.map((toc, idx) => (
- setTocIndex(idx)}
- active={idx === tocIndex}
- variant="body3"
- key={idx}
- >
- {toc.title}
-
- ))}
-
- ),
- [toc, tocIndex],
- )
-
- return (
-
-
- {_mobileToc}
- {_blocks}
-
-
- )
-}
-
-const ArticleContainer = styled.article`
- display: flex;
- position: relative;
- flex-direction: column;
- gap: 16px;
- max-width: 700px;
- margin-inline: 5%;
- padding-bottom: 80px;
-
- // temporary breakpoint
- @media (max-width: 1024px) {
- margin-inline: 16px;
- }
-`
-
-const TextContainer = styled.div`
- display: flex;
- flex-direction: column;
- gap: 16px;
- margin-top: 24px;
- margin-bottom: 80px;
-`
-
-const Row = styled.div`
- display: flex;
- flex-direction: row;
- align-items: center;
- gap: 8px;
- margin-bottom: 8px;
-`
-const TagContainer = styled.div`
- display: flex;
- gap: 8px;
-`
-
-const Content = styled(CustomTypography)<{ active: boolean }>`
- padding: 8px 14px;
- background-color: ${(p) =>
- p.active
- ? 'rgb(var(--lsd-theme-primary))'
- : 'rgb(var(--lsd-theme-secondary))'};
- color: ${(p) =>
- p.active
- ? 'rgb(var(--lsd-theme-secondary))'
- : 'rgb(var(--lsd-theme-primary))'};
-`
-
-const Reference = styled.div`
- display: flex;
- padding: 8px 14px;
- gap: 8px;
-`
-
-const ArticleFooterContainer = styled.div`
- margin-top: 16px;
-`
-
-const AuthorInfo = styled.div`
- display: flex;
- flex-direction: column;
- gap: 4px;
- margin-top: 8px;
-`
diff --git a/src/components/Article/Footer/Article.Footer.tsx b/src/components/Article/Footer/Article.Footer.tsx
new file mode 100644
index 0000000..cd7a7d5
--- /dev/null
+++ b/src/components/Article/Footer/Article.Footer.tsx
@@ -0,0 +1,43 @@
+import { ArticlePostData } from '@/types/data.types'
+import { useMemo } from 'react'
+import ArticleFootnotes from './Article.Footnotes'
+import { UnbodyGraphQl } from '@/lib/unbody/unbody-content.types'
+import styled from '@emotion/styled'
+import FromSameAuthorsArticles from './Article.FromSameAuthorsArticles'
+import ArticleRelatedArticles from './Article.RelatedArticles'
+
+const ArticleFooter = ({ data }: { data: ArticlePostData }) => {
+ const { article, relatedArticles, articlesFromSameAuthors } = data
+
+ const footnotes = useMemo(() => {
+ return (
+ article.blocks
+ // @ts-ignore
+ .flatMap((b) =>
+ b.__typename === UnbodyGraphQl.UnbodyDocumentTypeNames.TextBlock
+ ? b.footnotes
+ : [],
+ )
+ )
+ }, [article])
+
+ return (
+
+ }
+ />
+
+
+
+ )
+}
+
+const ArticleFooterContainer = styled.div`
+ margin-top: 16px;
+
+ & > div:not(:first-child) > div > button {
+ border-top: none;
+ }
+`
+
+export default ArticleFooter
diff --git a/src/components/Article/Footer/Article.Footnotes.tsx b/src/components/Article/Footer/Article.Footnotes.tsx
new file mode 100644
index 0000000..d9aaa51
--- /dev/null
+++ b/src/components/Article/Footer/Article.Footnotes.tsx
@@ -0,0 +1,36 @@
+import { Collapse } from '@/components/Collapse'
+import { UnbodyGraphQl } from '@/lib/unbody/unbody-content.types'
+import { Typography } from '@acid-info/lsd-react'
+import styled from '@emotion/styled'
+
+const ArticleFootnotes = ({
+ footnotes,
+}: {
+ footnotes: UnbodyGraphQl.Fragments.FootnoteItem[]
+}) =>
+ footnotes.length > 0 ? (
+
+ {footnotes.map((footnote, idx) => (
+
+
+
+
+ ))}
+
+ ) : null
+
+const Reference = styled.div`
+ display: flex;
+ padding: 8px 14px;
+ gap: 8px;
+`
+
+export default ArticleFootnotes
diff --git a/src/components/Article/Footer/Article.FromSameAuthorsArticles.tsx b/src/components/Article/Footer/Article.FromSameAuthorsArticles.tsx
new file mode 100644
index 0000000..ae0f3c5
--- /dev/null
+++ b/src/components/Article/Footer/Article.FromSameAuthorsArticles.tsx
@@ -0,0 +1,15 @@
+import { ArticleReference } from '@/components/ArticleReference'
+import { GoogleDocEnhanced } from '@/lib/unbody/unbody.types'
+import styles from '../Article.module.css'
+import { Collapse } from '@/components/Collapse'
+
+const FromSameAuthorsArticles = ({ data }: { data: GoogleDocEnhanced[] }) =>
+ data.length > 0 ? (
+
+ {data.map((article, idx) => (
+
+ ))}
+
+ ) : null
+
+export default FromSameAuthorsArticles
diff --git a/src/components/Article/Footer/Article.RelatedArticles.tsx b/src/components/Article/Footer/Article.RelatedArticles.tsx
new file mode 100644
index 0000000..a38c290
--- /dev/null
+++ b/src/components/Article/Footer/Article.RelatedArticles.tsx
@@ -0,0 +1,14 @@
+import { GoogleDocEnhanced } from '@/lib/unbody/unbody.types'
+import { ArticleReference } from '@/components/ArticleReference'
+import { Collapse } from '@/components/Collapse'
+
+const ArticleRelatedArticles = ({ data }: { data: GoogleDocEnhanced[] }) =>
+ data.length > 0 ? (
+
+ {data.map((article, idx) => (
+
+ ))}
+
+ ) : null
+
+export default ArticleRelatedArticles
diff --git a/src/components/Article/Header/Article.Author.tsx b/src/components/Article/Header/Article.Author.tsx
new file mode 100644
index 0000000..d8008b1
--- /dev/null
+++ b/src/components/Article/Header/Article.Author.tsx
@@ -0,0 +1,32 @@
+import { UnbodyGraphQl } from '@/lib/unbody/unbody-content.types'
+import { Typography } from '@acid-info/lsd-react'
+import styled from '@emotion/styled'
+
+const ArticleAuthor = ({
+ mention,
+}: {
+ mention: UnbodyGraphQl.Fragments.MentionItem
+}) => (
+
+
+ {mention.name}
+
+
+ {mention.emailAddress}
+
+
+)
+
+const AuthorInfo = styled.div`
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
+ margin-top: 8px;
+`
+
+export default ArticleAuthor
diff --git a/src/components/Article/Header/Article.Authors.tsx b/src/components/Article/Header/Article.Authors.tsx
new file mode 100644
index 0000000..6e2a0b0
--- /dev/null
+++ b/src/components/Article/Header/Article.Authors.tsx
@@ -0,0 +1,25 @@
+import { UnbodyGraphQl } from '@/lib/unbody/unbody-content.types'
+import ArticleAuthor from './Article.Author'
+import styled from '@emotion/styled'
+
+const ArticleAuthors = ({
+ mentions,
+}: {
+ mentions: UnbodyGraphQl.Fragments.MentionItem[]
+}) =>
+ mentions.length > 0 ? (
+
+ {mentions.map((mention) => (
+
+ ))}
+
+ ) : null
+
+const ArticleAuthorsContainer = styled.div`
+ margin-block: 24px;
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+`
+
+export default ArticleAuthors
diff --git a/src/components/Article/Header/Article.Header.tsx b/src/components/Article/Header/Article.Header.tsx
new file mode 100644
index 0000000..efb4fd6
--- /dev/null
+++ b/src/components/Article/Header/Article.Header.tsx
@@ -0,0 +1,67 @@
+import { GoogleDocEnhanced } from '@/lib/unbody/unbody.types'
+import { getArticleCover } from '@/utils/data.utils'
+import { useMemo } from 'react'
+import { ArticleImageBlockWrapper } from '../Article.ImageBlockWrapper'
+import { PostImageRatio } from '../../Post/Post'
+import ArticleStats from '../Article.Stats'
+import { Typography } from '@acid-info/lsd-react'
+import styled from '@emotion/styled'
+import ArticleTags from './Article.Tags'
+import ArticleAuthors from './Article.Authors'
+import ArticleSummary from './Article.Summary'
+
+const ArticleHeader = ({
+ title,
+ toc,
+ summary,
+ subtitle,
+ mentions,
+ tags,
+ modifiedAt,
+ blocks,
+}: GoogleDocEnhanced) => {
+ const date = new Date(modifiedAt)
+
+ const _thumbnail = useMemo(() => {
+ const coverImage = getArticleCover(blocks)
+ if (!coverImage) return null
+ return (
+
+ )
+ }, [blocks])
+
+ return (
+
+
+
+ {title}
+
+ {subtitle && {subtitle}}
+
+
+ {_thumbnail}
+
+
+ )
+}
+
+const CustomTypography = styled(Typography)`
+ text-overflow: ellipsis;
+ word-break: break-word;
+ white-space: pre-wrap;
+`
+
+const ArticleTitle = styled(CustomTypography)`
+ margin-bottom: 24px;
+`
+
+const ArticleSubtitle = styled(CustomTypography)``
+
+export default ArticleHeader
diff --git a/src/components/Article/Header/Article.Summary.tsx b/src/components/Article/Header/Article.Summary.tsx
new file mode 100644
index 0000000..09709a7
--- /dev/null
+++ b/src/components/Article/Header/Article.Summary.tsx
@@ -0,0 +1,16 @@
+import { Quote } from '@acid-info/lsd-react'
+import styled from '@emotion/styled'
+
+const ArticleSummary = ({ summary }: { summary: string }) => (
+ //TODO for ihor to work out the design for this
+
+ {summary}
+
+)
+
+const ArticleSummaryContainer = styled('div')`
+ padding-left: 40px;
+ margin-bottom: 32px;
+`
+
+export default ArticleSummary
diff --git a/src/components/Article/Header/Article.Tags.tsx b/src/components/Article/Header/Article.Tags.tsx
new file mode 100644
index 0000000..81bc22a
--- /dev/null
+++ b/src/components/Article/Header/Article.Tags.tsx
@@ -0,0 +1,21 @@
+import { Tag } from '@acid-info/lsd-react'
+import styled from '@emotion/styled'
+
+const ArticleTags = ({ tags }: { tags: string[] }) =>
+ tags.length > 0 ? (
+
+ {tags.map((tag) => (
+
+ {tag}
+
+ ))}
+
+ ) : null
+
+const TagContainer = styled.div`
+ display: flex;
+ gap: 8px;
+ margin-top: 16px;
+`
+
+export default ArticleTags
diff --git a/src/components/Article/index.ts b/src/components/Article/index.ts
index ad2cb33..11f02d7 100644
--- a/src/components/Article/index.ts
+++ b/src/components/Article/index.ts
@@ -1 +1 @@
-export { default as Article } from './Article'
+export { default as Article } from './Article.Body'
diff --git a/src/components/Article/tempData/index.tsx b/src/components/Article/tempData/index.tsx
index 758c1aa..e7e7417 100644
--- a/src/components/Article/tempData/index.tsx
+++ b/src/components/Article/tempData/index.tsx
@@ -1,5 +1,3 @@
-import { ArticleReferenceType } from '@/components/ArticleReference/ArticleReference'
-
// temporary type
export type ReferenceType = {
text: string
@@ -18,7 +16,7 @@ export const references: ReferenceType[] = [
},
]
-export const moreFromAuthor: ArticleReferenceType[] = [
+export const moreFromAuthor = [
{
title: 'How to Build a Practical Household Bike Generator',
author: 'Jason Freeman',
@@ -31,7 +29,7 @@ export const moreFromAuthor: ArticleReferenceType[] = [
},
]
-export const relatedArticles: ArticleReferenceType[] = [
+export const relatedArticles = [
{
title: 'How to Build a Practical Household Bike Generator',
author: 'Jason Freeman',
diff --git a/src/components/ArticleReference/ArticleReference.tsx b/src/components/ArticleReference/ArticleReference.tsx
index dbd4129..5ab9d67 100644
--- a/src/components/ArticleReference/ArticleReference.tsx
+++ b/src/components/ArticleReference/ArticleReference.tsx
@@ -1,4 +1,4 @@
-import { GoogleDocEnhanced, UnbodyGoogleDoc } from '@/lib/unbody/unbody.types'
+import { GoogleDocEnhanced } from '@/lib/unbody/unbody.types'
import { Typography } from '@acid-info/lsd-react'
import styled from '@emotion/styled'
@@ -24,7 +24,7 @@ export default function ArticleReference({
{/*TODO we need handle multiple authors for same article*/}
- {mentions[0].name}
+ {mentions[0]?.name}
•
diff --git a/src/containers/ArticleContainer.tsx b/src/containers/ArticleContainer.tsx
index 88532b2..626722d 100644
--- a/src/containers/ArticleContainer.tsx
+++ b/src/containers/ArticleContainer.tsx
@@ -1,14 +1,8 @@
import { TableOfContents } from '@/components/TableOfContents'
import styled from '@emotion/styled'
import { useState } from 'react'
-import { uiConfigs } from '@/configs/ui.configs'
import { ArticleContainerContext } from '@/containers/ArticleContainer.Context'
-import {
- GoogleDocEnhanced,
- UnbodyGoogleDoc,
- UnbodyTocItem,
-} from '@/lib/unbody/unbody.types'
-import ArticleBody from '@/components/Article/ArticleBody'
+import ArticleBody from '@/components/Article/Article.Body'
import { ArticlePostData } from '@/types/data.types'
interface Props {