mirror of
https://github.com/acid-info/logos-press-engine.git
synced 2025-02-23 22:58:08 +00:00
Merge branch 'main' into amir-fixes
This commit is contained in:
commit
e9cfbf8314
@ -1,3 +1,6 @@
|
|||||||
{
|
{
|
||||||
"extends": "next/core-web-vitals"
|
"extends": "next/core-web-vitals",
|
||||||
|
"rules": {
|
||||||
|
"@next/next/no-img-element": "off"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 31 KiB |
16
public/manifest.json
Normal file
16
public/manifest.json
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"short_name": "Logos Press Engine",
|
||||||
|
"name": "Logos Press Engine",
|
||||||
|
"start_url": "/",
|
||||||
|
"display": "standalone",
|
||||||
|
"theme_color": "#000000",
|
||||||
|
"background_color": "#ffffff",
|
||||||
|
"icons": [
|
||||||
|
{
|
||||||
|
"src": "./favicon.ico",
|
||||||
|
"sizes": "64x64",
|
||||||
|
"type": "image/icon",
|
||||||
|
"purpose": "any maskable"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -23,7 +23,7 @@ export default function ArticleBody({ data }: Props) {
|
|||||||
if (resultsNumber !== null) {
|
if (resultsNumber !== null) {
|
||||||
setResultsHelperText(data.article.title)
|
setResultsHelperText(data.article.title)
|
||||||
}
|
}
|
||||||
}, [resultsNumber])
|
}, [resultsNumber, data.article.title, setResultsHelperText])
|
||||||
|
|
||||||
const ids = searchResultBlocks?.map((block) => block.doc._additional.id)
|
const ids = searchResultBlocks?.map((block) => block.doc._additional.id)
|
||||||
|
|
||||||
|
@ -45,4 +45,5 @@ export const ArticleHeading = ({
|
|||||||
const Headline = styled(Typography)`
|
const Headline = styled(Typography)`
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
margin-top: 24px;
|
margin-top: 24px;
|
||||||
|
margin-bottom: 24px;
|
||||||
`
|
`
|
||||||
|
@ -3,7 +3,6 @@ import React from 'react'
|
|||||||
import { useArticleContainerContext } from '@/containers/ArticleContainer.Context'
|
import { useArticleContainerContext } from '@/containers/ArticleContainer.Context'
|
||||||
import { Typography } from '@acid-info/lsd-react'
|
import { Typography } from '@acid-info/lsd-react'
|
||||||
import styles from './Article.module.css'
|
import styles from './Article.module.css'
|
||||||
import { GoogleDocEnhanced } from '@/lib/unbody/unbody.types'
|
|
||||||
import { Collapse } from '../Collapse'
|
import { Collapse } from '../Collapse'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import { UnbodyGraphQl } from '@/lib/unbody/unbody-content.types'
|
import { UnbodyGraphQl } from '@/lib/unbody/unbody-content.types'
|
||||||
@ -13,7 +12,7 @@ type Props = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const MobileToc = ({ toc }: Props) => {
|
export const MobileToc = ({ toc }: Props) => {
|
||||||
const { tocId, setTocId } = useArticleContainerContext()
|
const { tocId } = useArticleContainerContext()
|
||||||
|
|
||||||
return toc?.length > 0 ? (
|
return toc?.length > 0 ? (
|
||||||
<Collapse className={styles.mobileToc} label="Contents">
|
<Collapse className={styles.mobileToc} label="Contents">
|
||||||
@ -23,7 +22,7 @@ export const MobileToc = ({ toc }: Props) => {
|
|||||||
key={idx}
|
key={idx}
|
||||||
active={tocId ? toc.href.substring(1) === tocId : idx === 0}
|
active={tocId ? toc.href.substring(1) === tocId : idx === 0}
|
||||||
>
|
>
|
||||||
<Typography variant="label2" genericFontFamily="sans-serif">
|
<CustomTypography variant="label2" genericFontFamily="sans-serif">
|
||||||
{toc.title}
|
{toc.title}
|
||||||
</Typography>
|
</Typography>
|
||||||
</TocItem>
|
</TocItem>
|
||||||
@ -44,8 +43,12 @@ const TocItem = styled(Link)<{ active: boolean }>`
|
|||||||
p.active
|
p.active
|
||||||
? 'rgb(var(--lsd-theme-primary))'
|
? 'rgb(var(--lsd-theme-primary))'
|
||||||
: 'rgb(var(--lsd-theme-secondary))'};
|
: 'rgb(var(--lsd-theme-secondary))'};
|
||||||
|
|
||||||
|
label {
|
||||||
|
text-decoration: none;
|
||||||
color: ${(p) =>
|
color: ${(p) =>
|
||||||
p.active
|
p.active
|
||||||
? 'rgb(var(--lsd-theme-secondary))'
|
? 'rgb(var(--lsd-theme-secondary))'
|
||||||
: 'rgb(var(--lsd-theme-primary))'};
|
: 'rgb(var(--lsd-theme-primary))'};
|
||||||
|
}
|
||||||
`
|
`
|
||||||
|
@ -31,7 +31,9 @@ const ArticleFooter = ({ data }: { data: ArticlePostData }) => {
|
|||||||
|
|
||||||
const ArticleFooterContainer = styled.div`
|
const ArticleFooterContainer = styled.div`
|
||||||
margin-top: 16px;
|
margin-top: 16px;
|
||||||
& > div:not(:first-child) > div > button {
|
|
||||||
|
& > div:not(:first-child) > div > button,
|
||||||
|
& > div:not(:first-child) > div {
|
||||||
border-top: none;
|
border-top: none;
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
@ -5,7 +5,7 @@ import { Collapse } from '@/components/Collapse'
|
|||||||
|
|
||||||
const FromSameAuthorsArticles = ({ data }: { data: GoogleDocEnhanced[] }) =>
|
const FromSameAuthorsArticles = ({ data }: { data: GoogleDocEnhanced[] }) =>
|
||||||
data.length > 0 ? (
|
data.length > 0 ? (
|
||||||
<Collapse className={styles.relatedArticles} label="From same authors">
|
<Collapse className={styles.relatedArticles} label="From The Same Authors">
|
||||||
{data.map((article, idx) => (
|
{data.map((article, idx) => (
|
||||||
<ArticleReference key={idx} data={article} />
|
<ArticleReference key={idx} data={article} />
|
||||||
))}
|
))}
|
||||||
|
@ -1,15 +1,20 @@
|
|||||||
import { GoogleDocEnhanced } from '@/lib/unbody/unbody.types'
|
import { GoogleDocEnhanced } from '@/lib/unbody/unbody.types'
|
||||||
import { Typography } from '@acid-info/lsd-react'
|
import { Typography } from '@acid-info/lsd-react'
|
||||||
import styled from '@emotion/styled'
|
import styled from '@emotion/styled'
|
||||||
|
import { Authors } from '../Authors'
|
||||||
|
import { AuthorsDirection } from '../Authors/Authors'
|
||||||
|
import Link from 'next/link'
|
||||||
|
import { useRouter } from 'next/router'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
data: GoogleDocEnhanced
|
data: GoogleDocEnhanced
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function ArticleReference({
|
export default function ArticleReference({
|
||||||
data: { title, modifiedAt, mentions },
|
data: { title, modifiedAt, mentions, slug },
|
||||||
...props
|
...props
|
||||||
}: Props) {
|
}: Props) {
|
||||||
|
const router = useRouter()
|
||||||
const localDate = new Date(modifiedAt).toLocaleString('en-GB', {
|
const localDate = new Date(modifiedAt).toLocaleString('en-GB', {
|
||||||
day: 'numeric',
|
day: 'numeric',
|
||||||
month: 'long',
|
month: 'long',
|
||||||
@ -17,31 +22,47 @@ export default function ArticleReference({
|
|||||||
})
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Reference {...props}>
|
<ReferenceLink href={slug} {...props}>
|
||||||
<Typography component="span" variant="body1">
|
<Typography component="span" variant="body1">
|
||||||
{title}
|
{title}
|
||||||
</Typography>
|
</Typography>
|
||||||
<div>
|
<Info>
|
||||||
<Typography variant="body3" genericFontFamily="sans-serif">
|
<Authors
|
||||||
{/*TODO we need handle multiple authors for same article*/}
|
flexDirection={AuthorsDirection.ROW}
|
||||||
{mentions[0]?.name}
|
gap={4}
|
||||||
</Typography>
|
mentions={mentions}
|
||||||
|
email={false}
|
||||||
|
/>
|
||||||
<Typography variant="body3">•</Typography>
|
<Typography variant="body3">•</Typography>
|
||||||
<Typography variant="body3" genericFontFamily="sans-serif">
|
<Typography variant="body3" genericFontFamily="sans-serif">
|
||||||
{localDate}
|
{localDate}
|
||||||
</Typography>
|
</Typography>
|
||||||
</div>
|
</Info>
|
||||||
</Reference>
|
</ReferenceLink>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const Reference = styled.div`
|
const ReferenceLink = styled(Link)`
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
padding: 8px 14px;
|
padding: 8px 14px;
|
||||||
border-bottom: 1px solid rgb(var(--lsd-border-primary));
|
border-bottom: 1px solid rgb(var(--lsd-border-primary));
|
||||||
|
text-decoration: none;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
&:last-child {
|
&:last-child {
|
||||||
border-bottom: none;
|
border-bottom: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
const Info = styled.div`
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
margin-top: 8px;
|
||||||
`
|
`
|
||||||
|
@ -2,25 +2,38 @@ import { UnbodyGraphQl } from '@/lib/unbody/unbody-content.types'
|
|||||||
import Author from './Author'
|
import Author from './Author'
|
||||||
import styled from '@emotion/styled'
|
import styled from '@emotion/styled'
|
||||||
|
|
||||||
|
export enum AuthorsDirection {
|
||||||
|
COLUMN = 'column',
|
||||||
|
ROW = 'row',
|
||||||
|
}
|
||||||
|
|
||||||
const Authors = ({
|
const Authors = ({
|
||||||
mentions,
|
mentions,
|
||||||
email,
|
email,
|
||||||
|
gap = 16,
|
||||||
|
flexDirection = AuthorsDirection.COLUMN,
|
||||||
}: {
|
}: {
|
||||||
mentions: UnbodyGraphQl.Fragments.MentionItem[]
|
mentions: UnbodyGraphQl.Fragments.MentionItem[]
|
||||||
email: boolean
|
email: boolean
|
||||||
}) =>
|
gap?: number
|
||||||
mentions?.length > 0 ? (
|
flexDirection?: AuthorsDirection
|
||||||
<AuthorsContainer>
|
}) => {
|
||||||
|
return mentions?.length > 0 ? (
|
||||||
|
<AuthorsContainer gap={gap} flexDirection={flexDirection}>
|
||||||
{mentions.map((mention) => (
|
{mentions.map((mention) => (
|
||||||
<Author key={mention.name} mention={mention} email={email} />
|
<Author key={mention.name} mention={mention} email={email} />
|
||||||
))}
|
))}
|
||||||
</AuthorsContainer>
|
</AuthorsContainer>
|
||||||
) : null
|
) : null
|
||||||
|
}
|
||||||
|
|
||||||
const AuthorsContainer = styled.div`
|
const AuthorsContainer = styled.div<{
|
||||||
|
gap: number
|
||||||
|
flexDirection: AuthorsDirection
|
||||||
|
}>`
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: ${({ flexDirection }) => flexDirection};
|
||||||
gap: 16px;
|
gap: ${({ gap }) => gap}px;
|
||||||
`
|
`
|
||||||
|
|
||||||
export default Authors
|
export default Authors
|
||||||
|
@ -1,8 +0,0 @@
|
|||||||
.collapse > div > button {
|
|
||||||
width: 100% !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.collapse > div {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
@ -1,21 +1,85 @@
|
|||||||
import { CollapseProps, Collapse as LsdCollapse } from '@acid-info/lsd-react'
|
import { ArrowDownIcon, ArrowUpIcon, Typography } from '@acid-info/lsd-react'
|
||||||
import styles from './Collapse.module.css'
|
|
||||||
import styled from '@emotion/styled'
|
import styled from '@emotion/styled'
|
||||||
import clsx from 'clsx'
|
import clsx from 'clsx'
|
||||||
|
import { useState } from 'react'
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
label: string
|
||||||
|
children: React.ReactNode
|
||||||
|
className?: string
|
||||||
|
onClick?: () => void
|
||||||
|
}
|
||||||
|
|
||||||
export default function Collapse({
|
export default function Collapse({
|
||||||
label,
|
label,
|
||||||
children,
|
children,
|
||||||
className,
|
className,
|
||||||
|
onClick,
|
||||||
...props
|
...props
|
||||||
}: CollapseProps) {
|
}: Props) {
|
||||||
|
const [open, setOpen] = useState(true)
|
||||||
|
|
||||||
|
const handleClick = (e: React.MouseEvent<HTMLElement>) => {
|
||||||
|
e.stopPropagation()
|
||||||
|
setOpen((prev) => !prev)
|
||||||
|
onClick && onClick()
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<LsdCollapse
|
<div {...props} className={clsx(className)}>
|
||||||
label={label}
|
<Header onClick={(e) => handleClick(e)}>
|
||||||
{...props}
|
<Label color="primary" component="label" variant="label1">
|
||||||
className={clsx(styles.collapse, className)}
|
{label}
|
||||||
>
|
</Label>
|
||||||
{children}
|
<Icon>
|
||||||
</LsdCollapse>
|
{open ? (
|
||||||
|
<ArrowUpIcon color="primary" />
|
||||||
|
) : (
|
||||||
|
<ArrowDownIcon color="primary" />
|
||||||
|
)}
|
||||||
|
</Icon>
|
||||||
|
</Header>
|
||||||
|
{open && <Content>{children}</Content>}
|
||||||
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const Header = styled.div`
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
cursor: pointer;
|
||||||
|
background: none;
|
||||||
|
border: 1px solid rgb(var(--lsd-border-primary));
|
||||||
|
|
||||||
|
height: 40px;
|
||||||
|
padding: 9px 17px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
const Label = styled(Typography)`
|
||||||
|
cursor: pointer;
|
||||||
|
margin: auto;
|
||||||
|
`
|
||||||
|
|
||||||
|
const Icon = styled.div`
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-end;
|
||||||
|
`
|
||||||
|
|
||||||
|
const Content = styled.div`
|
||||||
|
border: 1px solid rgb(var(--lsd-border-primary));
|
||||||
|
border-top: none;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
`
|
||||||
|
@ -1,24 +1,28 @@
|
|||||||
|
import { GoogleDocEnhanced } from '@/lib/unbody/unbody.types'
|
||||||
import { Typography } from '@acid-info/lsd-react'
|
import { Typography } from '@acid-info/lsd-react'
|
||||||
import styled from '@emotion/styled'
|
import styled from '@emotion/styled'
|
||||||
|
import Link from 'next/link'
|
||||||
|
import { Authors } from '../Authors'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
title: string
|
data: any
|
||||||
author: string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const ContentBlockBody = ({ title, author }: Props) => {
|
const ContentBlockBody = ({ data }: Props) => {
|
||||||
|
const firstDocument = data.document[0]
|
||||||
|
const mentions =
|
||||||
|
typeof firstDocument.mentions === 'string'
|
||||||
|
? JSON.parse(firstDocument.mentions)
|
||||||
|
: firstDocument.mentions
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BlockBodyContainer>
|
<BlockBodyContainer>
|
||||||
|
<Link href={`/article/${data.document[0].slug}#p-${data.order}`}>
|
||||||
<Typography variant="body1" component="div" genericFontFamily="serif">
|
<Typography variant="body1" component="div" genericFontFamily="serif">
|
||||||
{title}
|
{data.document[0].title}
|
||||||
</Typography>
|
|
||||||
<Typography
|
|
||||||
variant="body3"
|
|
||||||
component="div"
|
|
||||||
genericFontFamily="sans-serif"
|
|
||||||
>
|
|
||||||
{author}
|
|
||||||
</Typography>
|
</Typography>
|
||||||
|
</Link>
|
||||||
|
<Authors mentions={mentions} email={false} />
|
||||||
</BlockBodyContainer>
|
</BlockBodyContainer>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -27,6 +31,10 @@ const BlockBodyContainer = styled.div`
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
|
|
||||||
|
a {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
export default ContentBlockBody
|
export default ContentBlockBody
|
||||||
|
@ -1,9 +1,13 @@
|
|||||||
import styled from '@emotion/styled'
|
import styled from '@emotion/styled'
|
||||||
import { Typography } from '@acid-info/lsd-react'
|
import { Typography } from '@acid-info/lsd-react'
|
||||||
import { PostClassType } from '../Post/Post'
|
|
||||||
|
export enum BlockType {
|
||||||
|
TEXT = 'text',
|
||||||
|
IMAGE = 'image',
|
||||||
|
}
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
type: PostClassType
|
type: BlockType
|
||||||
date: string
|
date: string
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -11,7 +15,7 @@ const ContentBlockHeader = ({ type, date }: Props) => {
|
|||||||
return (
|
return (
|
||||||
<ContentBlockInfo>
|
<ContentBlockInfo>
|
||||||
<Typography variant="body3" genericFontFamily="sans-serif">
|
<Typography variant="body3" genericFontFamily="sans-serif">
|
||||||
{type.toUpperCase()}
|
{type === BlockType.TEXT ? 'PARAGRAPH' : 'IMAGE'}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography variant="body3">•</Typography>
|
<Typography variant="body3">•</Typography>
|
||||||
<Typography variant="body3" genericFontFamily="sans-serif">
|
<Typography variant="body3" genericFontFamily="sans-serif">
|
||||||
|
@ -1,35 +1,30 @@
|
|||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import styled from '@emotion/styled'
|
import styled from '@emotion/styled'
|
||||||
import Image from 'next/image'
|
|
||||||
|
|
||||||
import { SearchResultItem } from '@/types/data.types'
|
import { SearchResultItem } from '@/types/data.types'
|
||||||
import { UnbodyImageBlock } from '@/lib/unbody/unbody.types'
|
import { UnbodyImageBlock } from '@/lib/unbody/unbody.types'
|
||||||
|
|
||||||
import { GridItem } from '../Grid/Grid'
|
import { GridItem } from '../Grid/Grid'
|
||||||
import { PostClassType } from '../Post/Post'
|
import ContentBlockHeader, { BlockType } from './ContentBlock.Header'
|
||||||
import ContentBlockHeader from './ContentBlock.Header'
|
|
||||||
import ContentBlockBody from './ContentBlock.Body'
|
import ContentBlockBody from './ContentBlock.Body'
|
||||||
|
import { ResponsiveImage } from '../ResponsiveImage/ResponsiveImage'
|
||||||
|
|
||||||
type Props = Omit<SearchResultItem<UnbodyImageBlock>, 'score'>
|
type Props = Omit<SearchResultItem<UnbodyImageBlock>, 'score'>
|
||||||
|
|
||||||
const ImageBlock = ({ doc }: Props) => {
|
const ImageBlock = ({ doc }: Props) => {
|
||||||
return (
|
return (
|
||||||
<CustomGridItem className="w-2">
|
<CustomGridItem className="w-2">
|
||||||
<BlockLink href={`/article/${doc.document[0].slug}`}>
|
{/* TODO: order not working for images */}
|
||||||
<Container>
|
<Container>
|
||||||
<ImageContainer>
|
<Link href={`/article/${doc.document[0].slug}#p-${doc.order}`}>
|
||||||
<Image fill src={doc.url} alt={doc.alt} />
|
<ResponsiveImage data={doc} />
|
||||||
</ImageContainer>
|
</Link>
|
||||||
<ContentBlockHeader
|
<ContentBlockHeader
|
||||||
type={PostClassType.ARTICLE}
|
type={BlockType.IMAGE}
|
||||||
date={doc?.document[0].modifiedAt}
|
date={doc?.document[0].modifiedAt}
|
||||||
/>
|
/>
|
||||||
<ContentBlockBody
|
<ContentBlockBody data={doc} />
|
||||||
title={doc.document[0].title}
|
|
||||||
author="Jason Freeman"
|
|
||||||
/>
|
|
||||||
</Container>
|
</Container>
|
||||||
</BlockLink>
|
|
||||||
</CustomGridItem>
|
</CustomGridItem>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -40,10 +35,6 @@ const CustomGridItem = styled(GridItem)`
|
|||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
const BlockLink = styled(Link)`
|
|
||||||
text-decoration: none;
|
|
||||||
`
|
|
||||||
|
|
||||||
const Container = styled.div`
|
const Container = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
@ -7,7 +7,7 @@ import { UnbodyTextBlock } from '@/lib/unbody/unbody.types'
|
|||||||
import { GridItem } from '../Grid/Grid'
|
import { GridItem } from '../Grid/Grid'
|
||||||
import { Typography } from '@acid-info/lsd-react'
|
import { Typography } from '@acid-info/lsd-react'
|
||||||
import { PostClassType } from '../Post/Post'
|
import { PostClassType } from '../Post/Post'
|
||||||
import ContentBlockHeader from './ContentBlock.Header'
|
import ContentBlockHeader, { BlockType } from './ContentBlock.Header'
|
||||||
import ContentBlockBody from './ContentBlock.Body'
|
import ContentBlockBody from './ContentBlock.Body'
|
||||||
|
|
||||||
type Props = Omit<SearchResultItem<UnbodyTextBlock>, 'score'>
|
type Props = Omit<SearchResultItem<UnbodyTextBlock>, 'score'>
|
||||||
@ -15,21 +15,16 @@ type Props = Omit<SearchResultItem<UnbodyTextBlock>, 'score'>
|
|||||||
const TextBlock = ({ doc }: Props) => {
|
const TextBlock = ({ doc }: Props) => {
|
||||||
return (
|
return (
|
||||||
<GridItem className="w-4">
|
<GridItem className="w-4">
|
||||||
<BlockLink href={`/article/${doc.document[0].slug}`}>
|
|
||||||
<Container>
|
<Container>
|
||||||
<ContentBlockHeader
|
<ContentBlockHeader
|
||||||
type={PostClassType.ARTICLE}
|
type={BlockType.TEXT}
|
||||||
date={doc?.document[0].modifiedAt}
|
date={doc?.document[0].modifiedAt}
|
||||||
/>
|
/>
|
||||||
<Typography variant="body2" genericFontFamily="sans-serif">
|
<Typography variant="body2" genericFontFamily="sans-serif">
|
||||||
{doc.text}
|
{doc.text}
|
||||||
</Typography>
|
</Typography>
|
||||||
<ContentBlockBody
|
<ContentBlockBody data={doc} />
|
||||||
title={doc.document[0].title}
|
|
||||||
author="Jason Freeman"
|
|
||||||
/>
|
|
||||||
</Container>
|
</Container>
|
||||||
</BlockLink>
|
|
||||||
</GridItem>
|
</GridItem>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@ type Props = {
|
|||||||
|
|
||||||
const FeaturedPost = ({ post }: Props) => {
|
const FeaturedPost = ({ post }: Props) => {
|
||||||
return (
|
return (
|
||||||
<Grid>
|
<CustomGrid>
|
||||||
<GridItem className="w-16">
|
<GridItem className="w-16">
|
||||||
<PostLink href={`/article/${post.slug}`}>
|
<PostLink href={`/article/${post.slug}`}>
|
||||||
<PostWrapper>
|
<PostWrapper>
|
||||||
@ -32,10 +32,14 @@ const FeaturedPost = ({ post }: Props) => {
|
|||||||
</PostWrapper>
|
</PostWrapper>
|
||||||
</PostLink>
|
</PostLink>
|
||||||
</GridItem>
|
</GridItem>
|
||||||
</Grid>
|
</CustomGrid>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const CustomGrid = styled(Grid)`
|
||||||
|
margin-bottom: 108px;
|
||||||
|
`
|
||||||
|
|
||||||
const PostWrapper = styled.div`
|
const PostWrapper = styled.div`
|
||||||
margin-top: 16px;
|
margin-top: 16px;
|
||||||
padding: 16px 0;
|
padding: 16px 0;
|
||||||
@ -43,8 +47,4 @@ const PostWrapper = styled.div`
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
`
|
`
|
||||||
|
|
||||||
const PostLink = styled(Link)`
|
|
||||||
text-decoration: none;
|
|
||||||
`
|
|
||||||
|
|
||||||
export default FeaturedPost
|
export default FeaturedPost
|
||||||
|
@ -31,6 +31,7 @@ export default function FilterTags(props: FilterTagsProps) {
|
|||||||
|
|
||||||
const Container = styled.div`
|
const Container = styled.div`
|
||||||
padding: 8px 0;
|
padding: 8px 0;
|
||||||
|
max-width: 100%;
|
||||||
`
|
`
|
||||||
|
|
||||||
const Tags = styled.div`
|
const Tags = styled.div`
|
||||||
|
@ -22,6 +22,7 @@ const Container = styled.div`
|
|||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
|
text-align: left;
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
|
@ -7,8 +7,6 @@ const Main = ({ children }: PropsWithChildren) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const Container = styled.main`
|
const Container = styled.main`
|
||||||
//display: flex;
|
|
||||||
//justify-content: center;
|
|
||||||
margin-block: ${uiConfigs.postSectionMargin}px;
|
margin-block: ${uiConfigs.postSectionMargin}px;
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
margin-right: auto;
|
margin-right: auto;
|
||||||
|
@ -5,6 +5,7 @@ import { SunIcon } from '../Icons/SunIcon'
|
|||||||
import { MoonIcon } from '../Icons/MoonIcon'
|
import { MoonIcon } from '../Icons/MoonIcon'
|
||||||
import { useRouter } from 'next/router'
|
import { useRouter } from 'next/router'
|
||||||
import { uiConfigs } from '@/configs/ui.configs'
|
import { uiConfigs } from '@/configs/ui.configs'
|
||||||
|
import Link from 'next/link'
|
||||||
|
|
||||||
interface NavbarProps {
|
interface NavbarProps {
|
||||||
isDark: boolean
|
isDark: boolean
|
||||||
@ -12,10 +13,9 @@ interface NavbarProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function Navbar({ isDark, toggle }: NavbarProps) {
|
export default function Navbar({ isDark, toggle }: NavbarProps) {
|
||||||
const router = useRouter()
|
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
<LogosIconContainer onClick={() => router.push('/')}>
|
<LogosIconContainer href={'/'}>
|
||||||
<LogosIcon color="primary" />
|
<LogosIcon color="primary" />
|
||||||
</LogosIconContainer>
|
</LogosIconContainer>
|
||||||
<Icons>
|
<Icons>
|
||||||
@ -38,10 +38,11 @@ const Container = styled.nav`
|
|||||||
border-bottom: 1px solid rgb(var(--lsd-theme-primary));
|
border-bottom: 1px solid rgb(var(--lsd-theme-primary));
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0;
|
top: 0;
|
||||||
width: calc(100% + 16px);
|
width: 100%;
|
||||||
max-width: ${uiConfigs.maxContainerWidth + 16}px;
|
max-width: ${uiConfigs.maxContainerWidth + 40}px; // TBD
|
||||||
background: rgb(var(--lsd-surface-primary));
|
background: rgb(var(--lsd-surface-primary));
|
||||||
z-index: 100;
|
z-index: 100;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
// to center-align logo
|
// to center-align logo
|
||||||
&:last-child {
|
&:last-child {
|
||||||
@ -51,13 +52,13 @@ const Container = styled.nav`
|
|||||||
// to center-align logo
|
// to center-align logo
|
||||||
&:before {
|
&:before {
|
||||||
content: 'D';
|
content: 'D';
|
||||||
|
width: 54px;
|
||||||
margin: 1px auto 1px 1px;
|
margin: 1px auto 1px 1px;
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
padding: 1px;
|
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
const LogosIconContainer = styled.div`
|
const LogosIconContainer = styled(Link)`
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
@ -1,15 +1,23 @@
|
|||||||
import styled from '@emotion/styled'
|
import styled from '@emotion/styled'
|
||||||
import { FilterTags } from '@/components/FilterTags'
|
import { FilterTags } from '@/components/FilterTags'
|
||||||
import { useSearchBarContext } from '@/context/searchbar.context'
|
import { useSearchBarContext } from '@/context/searchbar.context'
|
||||||
|
import { useRouter } from 'next/router'
|
||||||
|
|
||||||
export const NavbarFiller = () => {
|
export const NavbarFiller = () => {
|
||||||
|
const router = useRouter()
|
||||||
const { tags } = useSearchBarContext()
|
const { tags } = useSearchBarContext()
|
||||||
|
|
||||||
|
const onTagClick = (tag: string) => {
|
||||||
|
router.push(`/search?topics=${tag}`)
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<NavbarFillerContainer>
|
<NavbarFillerContainer>
|
||||||
<FilterTags tags={tags} selectedTags={[]} />
|
<FilterTags onTagClick={onTagClick} tags={tags} selectedTags={[]} />
|
||||||
</NavbarFillerContainer>
|
</NavbarFillerContainer>
|
||||||
)
|
)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const NavbarFillerContainer = styled.div`
|
export const NavbarFillerContainer = styled.div`
|
||||||
height: var(--lpe-nav-rendered-height);
|
height: var(--lpe-nav-rendered-height);
|
||||||
margin: auto;
|
margin: auto;
|
||||||
|
@ -17,6 +17,7 @@ import {
|
|||||||
ResponsiveImage,
|
ResponsiveImage,
|
||||||
ResponsiveImageProps,
|
ResponsiveImageProps,
|
||||||
} from '../ResponsiveImage/ResponsiveImage'
|
} from '../ResponsiveImage/ResponsiveImage'
|
||||||
|
import Link from 'next/link'
|
||||||
|
|
||||||
export enum PostImageRatio {
|
export enum PostImageRatio {
|
||||||
PORTRAIT = 'portrait',
|
PORTRAIT = 'portrait',
|
||||||
@ -95,22 +96,25 @@ export default function Post({
|
|||||||
title,
|
title,
|
||||||
description,
|
description,
|
||||||
mentions,
|
mentions,
|
||||||
|
slug,
|
||||||
tags = [],
|
tags = [],
|
||||||
},
|
},
|
||||||
...props
|
...props
|
||||||
}: PostProps) {
|
}: PostProps) {
|
||||||
const date = new Date(dateStr)
|
const _date = useMemo(() => new Date(dateStr), [dateStr])
|
||||||
|
|
||||||
const _title = useMemo(
|
const _title = useMemo(
|
||||||
() => (
|
() => (
|
||||||
|
<TitleLink href={`/article/${slug}`}>
|
||||||
<CustomTypography
|
<CustomTypography
|
||||||
variant={size === PostSize.SMALL ? 'h4' : 'h2'}
|
variant={size === PostSize.SMALL ? 'h4' : 'h2'}
|
||||||
genericFontFamily="serif"
|
genericFontFamily="serif"
|
||||||
>
|
>
|
||||||
{title}
|
{title}
|
||||||
</CustomTypography>
|
</CustomTypography>
|
||||||
|
</TitleLink>
|
||||||
),
|
),
|
||||||
[title, size],
|
[title, size, slug],
|
||||||
)
|
)
|
||||||
|
|
||||||
const _description = useMemo(
|
const _description = useMemo(
|
||||||
@ -126,19 +130,23 @@ export default function Post({
|
|||||||
const _thumbnail = useMemo(() => {
|
const _thumbnail = useMemo(() => {
|
||||||
if (!showImage || !coverImage) return null
|
if (!showImage || !coverImage) return null
|
||||||
if (postType === PostType.BODY) {
|
if (postType === PostType.BODY) {
|
||||||
return <ResponsiveImage {...imageProps} data={coverImage} />
|
|
||||||
} else {
|
|
||||||
// TBD
|
|
||||||
// @jinho not sure what this is for?
|
|
||||||
return (
|
return (
|
||||||
<ThumbnailContainer aspectRatio={aspectRatio}>
|
<Link href={`/article/${slug}`}>
|
||||||
<Thumbnail fill src={coverImage.url} alt={coverImage.alt} />
|
<ResponsiveImage {...imageProps} data={coverImage} />
|
||||||
|
</Link>
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Link href={`/article/${slug}`}>
|
||||||
|
<ResponsiveImage data={coverImage} alt={coverImage.alt} />
|
||||||
|
</Link>
|
||||||
{_title}
|
{_title}
|
||||||
{_description}
|
{_description}
|
||||||
</ThumbnailContainer>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}, [showImage, coverImage, aspectRatio, postType, _title, _description])
|
}, [slug, showImage, coverImage, postType, imageProps, _title, _description])
|
||||||
|
|
||||||
const _header = useMemo(() => {
|
const _header = useMemo(() => {
|
||||||
if (postType === 'body')
|
if (postType === 'body')
|
||||||
@ -151,7 +159,7 @@ export default function Post({
|
|||||||
</Typography>
|
</Typography>
|
||||||
<Typography variant="body3">•</Typography>
|
<Typography variant="body3">•</Typography>
|
||||||
<Typography variant="body3" genericFontFamily="sans-serif">
|
<Typography variant="body3" genericFontFamily="sans-serif">
|
||||||
{date.toLocaleString('en-GB', {
|
{_date.toLocaleString('en-GB', {
|
||||||
day: 'numeric',
|
day: 'numeric',
|
||||||
month: 'long', // TODO: Should be uppercase
|
month: 'long', // TODO: Should be uppercase
|
||||||
year: 'numeric',
|
year: 'numeric',
|
||||||
@ -162,7 +170,7 @@ export default function Post({
|
|||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}, [postType, classType, date, _title])
|
}, [postType, classType, _date, _title])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container {...props}>
|
<Container {...props}>
|
||||||
@ -191,21 +199,6 @@ const Container = styled.div`
|
|||||||
gap: 16px;
|
gap: 16px;
|
||||||
`
|
`
|
||||||
|
|
||||||
// @Jinho, I have implemented the ResponsiveImage component, so I guess this is not needed anymore?
|
|
||||||
const ThumbnailContainer = styled.div<{
|
|
||||||
aspectRatio: PostImageRatio
|
|
||||||
}>`
|
|
||||||
aspect-ratio: ${(p) =>
|
|
||||||
p.aspectRatio
|
|
||||||
? PostImageRatioOptions[p.aspectRatio]
|
|
||||||
: PostImageRatioOptions[PostImageRatio.PORTRAIT]};
|
|
||||||
position: relative;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
max-height: 458px; // temporary max-height based on the Figma design's max height
|
|
||||||
overflow: hidden;
|
|
||||||
`
|
|
||||||
|
|
||||||
const Thumbnail = styled(Image)`
|
const Thumbnail = styled(Image)`
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
`
|
`
|
||||||
@ -229,3 +222,7 @@ const PodcastAuthor = styled.div`
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
`
|
`
|
||||||
|
|
||||||
|
const TitleLink = styled(Link)`
|
||||||
|
text-decoration: none;
|
||||||
|
`
|
||||||
|
@ -90,7 +90,3 @@ const PostWrapper = styled.div`
|
|||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
const PostLink = styled(Link)`
|
|
||||||
text-decoration: none;
|
|
||||||
`
|
|
||||||
|
@ -1,21 +1,21 @@
|
|||||||
import useIsDarkState from '@/states/isDarkState/isDarkState'
|
import useIsDarkState from '@/states/isDarkState/isDarkState'
|
||||||
import { defaultThemes } from '@acid-info/lsd-react'
|
import { defaultThemes } from '@acid-info/lsd-react'
|
||||||
import NextNProgress from 'nextjs-progressbar'
|
import NextNProgress from 'nextjs-progressbar'
|
||||||
import { useEffect, useState } from 'react'
|
import { useCallback, useEffect, useState } from 'react'
|
||||||
|
|
||||||
export const ProgressBar = () => {
|
export const ProgressBar = () => {
|
||||||
const isDark = useIsDarkState().get()
|
const isDark = useIsDarkState().get()
|
||||||
|
|
||||||
const getColor = () => {
|
const getColor = useCallback(() => {
|
||||||
if (isDark) {
|
if (isDark) {
|
||||||
return `rgb(${defaultThemes.dark.palette.primary})`
|
return `rgb(${defaultThemes.dark.palette.primary})`
|
||||||
} else {
|
} else {
|
||||||
return `rgb(${defaultThemes.dark.palette.secondary})`
|
return `rgb(${defaultThemes.dark.palette.secondary})`
|
||||||
}
|
}
|
||||||
}
|
}, [isDark])
|
||||||
|
|
||||||
const [color, setColor] = useState(getColor())
|
const [color, setColor] = useState(getColor())
|
||||||
useEffect(() => setColor(getColor()), [isDark])
|
useEffect(() => setColor(getColor()), [isDark, getColor])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<NextNProgress
|
<NextNProgress
|
||||||
|
@ -19,7 +19,10 @@ export default function RelatedContent({ data }: Props) {
|
|||||||
>
|
>
|
||||||
<Grid>
|
<Grid>
|
||||||
{data.data.map(
|
{data.data.map(
|
||||||
(block: SearchResultItem<UnbodyImageBlock | UnbodyTextBlock>) => {
|
(
|
||||||
|
block: SearchResultItem<UnbodyImageBlock | UnbodyTextBlock>,
|
||||||
|
idx: number,
|
||||||
|
) => {
|
||||||
if (!block.doc.document || !block.doc.document[0]) return null
|
if (!block.doc.document || !block.doc.document[0]) return null
|
||||||
|
|
||||||
let refArticle = null
|
let refArticle = null
|
||||||
@ -28,9 +31,9 @@ export default function RelatedContent({ data }: Props) {
|
|||||||
}
|
}
|
||||||
switch (block.doc.__typename) {
|
switch (block.doc.__typename) {
|
||||||
case UnbodyGraphQl.UnbodyDocumentTypeNames.TextBlock:
|
case UnbodyGraphQl.UnbodyDocumentTypeNames.TextBlock:
|
||||||
return <TextBlock doc={block.doc} />
|
return <TextBlock key={`text-${idx}`} doc={block.doc} />
|
||||||
case UnbodyGraphQl.UnbodyDocumentTypeNames.ImageBlock: {
|
case UnbodyGraphQl.UnbodyDocumentTypeNames.ImageBlock: {
|
||||||
return <ImageBlock doc={block.doc} />
|
return <ImageBlock key={`image-${idx}`} doc={block.doc} />
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -11,12 +11,14 @@ export type ResponsiveImageProps = {
|
|||||||
|
|
||||||
export type Props = {
|
export type Props = {
|
||||||
data: UnbodyImageBlock | ImageBlockEnhanced
|
data: UnbodyImageBlock | ImageBlockEnhanced
|
||||||
|
alt?: string
|
||||||
} & ResponsiveImageProps
|
} & ResponsiveImageProps
|
||||||
|
|
||||||
export const ResponsiveImage = ({
|
export const ResponsiveImage = ({
|
||||||
data,
|
data,
|
||||||
height = '100%',
|
height = '100%',
|
||||||
fill = false,
|
fill = false,
|
||||||
|
alt = 'alt',
|
||||||
nextImageProps,
|
nextImageProps,
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const [loaded, setLoaded] = useState(false)
|
const [loaded, setLoaded] = useState(false)
|
||||||
@ -46,13 +48,8 @@ export const ResponsiveImage = ({
|
|||||||
background: 'red',
|
background: 'red',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Image
|
<img src={lazyUrl} alt={alt} />
|
||||||
src={lazyUrl}
|
<Image {...imageProps} alt={alt} />
|
||||||
alt={data.alt}
|
|
||||||
width={data.width}
|
|
||||||
height={data.height}
|
|
||||||
/>
|
|
||||||
<Image {...imageProps} />
|
|
||||||
</Container>
|
</Container>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -54,10 +54,8 @@ export default function Searchbar(props: SearchbarProps) {
|
|||||||
|
|
||||||
const isArticlePage = router.pathname === '/article/[slug]'
|
const isArticlePage = router.pathname === '/article/[slug]'
|
||||||
|
|
||||||
const performSearch = async (
|
const performSearch = useCallback(
|
||||||
q: string = query,
|
async (q: string = query, _filterTags: string[] = filterTags) => {
|
||||||
_filterTags: string[] = filterTags,
|
|
||||||
) => {
|
|
||||||
//if it is article page, just call onSearch
|
//if it is article page, just call onSearch
|
||||||
if (isArticlePage) {
|
if (isArticlePage) {
|
||||||
if (onSearch) {
|
if (onSearch) {
|
||||||
@ -77,7 +75,9 @@ export default function Searchbar(props: SearchbarProps) {
|
|||||||
undefined,
|
undefined,
|
||||||
{ shallow: true },
|
{ shallow: true },
|
||||||
)
|
)
|
||||||
}
|
},
|
||||||
|
[isArticlePage, router, filterTags, onSearch, query],
|
||||||
|
)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setQuery(extractQueryFromQuery(router.query))
|
setQuery(extractQueryFromQuery(router.query))
|
||||||
@ -87,7 +87,7 @@ export default function Searchbar(props: SearchbarProps) {
|
|||||||
} else {
|
} else {
|
||||||
setSearchScope(ESearchScope.GLOBAL)
|
setSearchScope(ESearchScope.GLOBAL)
|
||||||
}
|
}
|
||||||
}, [router.query.query, router.query.topics])
|
}, [router.query, router.query.topics, router.pathname])
|
||||||
|
|
||||||
const performClear = useCallback(() => {
|
const performClear = useCallback(() => {
|
||||||
if (!isArticlePage) {
|
if (!isArticlePage) {
|
||||||
@ -99,7 +99,7 @@ export default function Searchbar(props: SearchbarProps) {
|
|||||||
setFilterTags([])
|
setFilterTags([])
|
||||||
setActive(false)
|
setActive(false)
|
||||||
onReset && onReset()
|
onReset && onReset()
|
||||||
}, [setQuery, setFilterTags])
|
}, [isArticlePage, onReset, performSearch, setQuery, setFilterTags])
|
||||||
|
|
||||||
const handleTagClick = (tag: string) => {
|
const handleTagClick = (tag: string) => {
|
||||||
let newSelectedTags = [...filterTags]
|
let newSelectedTags = [...filterTags]
|
||||||
|
@ -40,7 +40,7 @@ export function SearchbarContainer({ children, onUnfocus = nope }: Props) {
|
|||||||
|
|
||||||
const SearchBarWrapper = styled.div<Props>`
|
const SearchBarWrapper = styled.div<Props>`
|
||||||
display: block;
|
display: block;
|
||||||
width: calc(100% - 16px);
|
width: 100%;
|
||||||
background: rgb(var(--lsd-surface-primary));
|
background: rgb(var(--lsd-surface-primary));
|
||||||
border-bottom: 1px solid rgb(var(--lsd-border-primary));
|
border-bottom: 1px solid rgb(var(--lsd-border-primary));
|
||||||
border-top: 1px solid rgb(var(--lsd-border-primary));
|
border-top: 1px solid rgb(var(--lsd-border-primary));
|
||||||
@ -53,5 +53,6 @@ const SearchBarWrapper = styled.div<Props>`
|
|||||||
position: fixed;
|
position: fixed;
|
||||||
top: ${uiConfigs.navbarRenderedHeight - 1}px;
|
top: ${uiConfigs.navbarRenderedHeight - 1}px;
|
||||||
z-index: 100;
|
z-index: 100;
|
||||||
|
max-width: ${uiConfigs.maxContainerWidth + 40}px; // TBD
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
@ -9,7 +9,7 @@ type Props = PropsWithChildren<{
|
|||||||
|
|
||||||
export const Section = ({ title, subtitle, children, ...props }: Props) => {
|
export const Section = ({ title, subtitle, children, ...props }: Props) => {
|
||||||
return (
|
return (
|
||||||
<section style={{ width: '100%' }} {...props}>
|
<SectionContainer {...props}>
|
||||||
<Container>
|
<Container>
|
||||||
<Typography genericFontFamily="sans-serif" variant="body2">
|
<Typography genericFontFamily="sans-serif" variant="body2">
|
||||||
{title}
|
{title}
|
||||||
@ -28,10 +28,19 @@ export const Section = ({ title, subtitle, children, ...props }: Props) => {
|
|||||||
)}
|
)}
|
||||||
</Container>
|
</Container>
|
||||||
{children}
|
{children}
|
||||||
</section>
|
</SectionContainer>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const SectionContainer = styled.section`
|
||||||
|
width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
padding-inline: 16px;
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
const Container = styled.div`
|
const Container = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
@ -24,7 +24,6 @@ export default function TableOfContents({ contents, ...props }: Props) {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const onHashChangeStart = (url: string) => {
|
const onHashChangeStart = (url: string) => {
|
||||||
const hash = url.split('#')[1]
|
const hash = url.split('#')[1]
|
||||||
console.log('hash', contents)
|
|
||||||
if (hash) {
|
if (hash) {
|
||||||
setTocId(hash)
|
setTocId(hash)
|
||||||
} else {
|
} else {
|
||||||
@ -35,7 +34,7 @@ export default function TableOfContents({ contents, ...props }: Props) {
|
|||||||
return () => {
|
return () => {
|
||||||
router.events.off('hashChangeStart', onHashChangeStart)
|
router.events.off('hashChangeStart', onHashChangeStart)
|
||||||
}
|
}
|
||||||
}, [router.events])
|
}, [setTocId, router.events])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container
|
<Container
|
||||||
@ -116,4 +115,8 @@ const TocItem = styled(Link)<{ active: boolean }>`
|
|||||||
? '1px solid rgb(var(--lsd-border-primary))'
|
? '1px solid rgb(var(--lsd-border-primary))'
|
||||||
: '1px solid transparent'};
|
: '1px solid transparent'};
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
|
label {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
`
|
`
|
||||||
|
@ -1,44 +1,38 @@
|
|||||||
import { addTopicsToQuery } from '@/utils/search.utils'
|
import { addTopicsToQuery } from '@/utils/search.utils'
|
||||||
import { Tag } from '@acid-info/lsd-react'
|
import { Tag } from '@acid-info/lsd-react'
|
||||||
import styled from '@emotion/styled'
|
import styled from '@emotion/styled'
|
||||||
|
import Link from 'next/link'
|
||||||
import { useRouter } from 'next/router'
|
import { useRouter } from 'next/router'
|
||||||
|
|
||||||
const Tags = ({ tags }: { tags: string[] }) => {
|
const Tags = ({ tags }: { tags: string[] }) => {
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
const { query } = router
|
||||||
const onTagClick = (e: React.MouseEvent<HTMLElement>, tag: string) => {
|
const { topics } = query
|
||||||
e.preventDefault()
|
|
||||||
router.push(
|
|
||||||
{
|
|
||||||
pathname: '/search',
|
|
||||||
query: {
|
|
||||||
...addTopicsToQuery([tag]),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
undefined,
|
|
||||||
{ shallow: true },
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return tags.length > 0 ? (
|
return tags.length > 0 ? (
|
||||||
<TagContainer>
|
<TagsContainer>
|
||||||
{tags.map((tag) => (
|
{tags.map((tag, idx) => (
|
||||||
|
<Link key={`tag-${idx}`} href={`/search?topics=${tag}`}>
|
||||||
<Tag
|
<Tag
|
||||||
onClick={(e) => onTagClick(e, tag)}
|
|
||||||
size="small"
|
size="small"
|
||||||
disabled={false}
|
disabled={false}
|
||||||
key={tag}
|
variant={topics?.includes(tag) ? 'filled' : 'outlined'}
|
||||||
>
|
>
|
||||||
{tag}
|
{tag}
|
||||||
</Tag>
|
</Tag>
|
||||||
|
</Link>
|
||||||
))}
|
))}
|
||||||
</TagContainer>
|
</TagsContainer>
|
||||||
) : null
|
) : null
|
||||||
}
|
}
|
||||||
|
|
||||||
const TagContainer = styled.div`
|
const TagsContainer = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
|
|
||||||
|
a {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
export default Tags
|
export default Tags
|
||||||
|
@ -32,7 +32,7 @@ export default function App({ Component, pageProps }: AppLayoutProps) {
|
|||||||
return (
|
return (
|
||||||
<ThemeProvider theme={isDark ? defaultThemes.dark : defaultThemes.light}>
|
<ThemeProvider theme={isDark ? defaultThemes.dark : defaultThemes.light}>
|
||||||
<Head>
|
<Head>
|
||||||
<title>Acid</title>
|
<title>Logos Press Engine</title>
|
||||||
<meta
|
<meta
|
||||||
name="viewport"
|
name="viewport"
|
||||||
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0"
|
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0"
|
||||||
|
@ -27,7 +27,7 @@ export default async function handler() {
|
|||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<img width="1200" height="630" src={srcBlob} />
|
<img width="1200" height="630" src={srcBlob} alt="og-image" />
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
{
|
{
|
||||||
|
@ -72,6 +72,8 @@ export default function SearchPage({
|
|||||||
articles.reset(initialArticles)
|
articles.reset(initialArticles)
|
||||||
blocks.reset(initialBlocks)
|
blocks.reset(initialBlocks)
|
||||||
}
|
}
|
||||||
|
// if we follow the eslint, we will have an infinite loop
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [mounted, router.query])
|
}, [mounted, router.query])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -16,6 +16,7 @@ export const getSearchBlocksQuery = (args: UnbodyGetFilters = defaultArgs) =>
|
|||||||
text
|
text
|
||||||
tagName
|
tagName
|
||||||
classNames
|
classNames
|
||||||
|
order
|
||||||
document{
|
document{
|
||||||
...on GoogleDoc{
|
...on GoogleDoc{
|
||||||
title
|
title
|
||||||
@ -36,6 +37,7 @@ export const getSearchBlocksQuery = (args: UnbodyGetFilters = defaultArgs) =>
|
|||||||
width
|
width
|
||||||
height
|
height
|
||||||
alt
|
alt
|
||||||
|
order
|
||||||
document{
|
document{
|
||||||
...on GoogleDoc{
|
...on GoogleDoc{
|
||||||
title
|
title
|
||||||
|
Loading…
x
Reference in New Issue
Block a user