Skip to content

Commit

Permalink
add news
Browse files Browse the repository at this point in the history
  • Loading branch information
KishiTheMechanic committed Nov 4, 2024
1 parent d83eb24 commit b133caa
Show file tree
Hide file tree
Showing 149 changed files with 330 additions and 25 deletions.
16 changes: 14 additions & 2 deletions components/ui/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,23 @@ interface ButtonProps
buttonType?: 'default' | 'icon' | 'small'
}

const Button = ({ variant, buttonType, children, ...props }: ButtonProps) => {
const Button = (
{ variant = 'default', buttonType = 'default', children, ...props }:
ButtonProps,
) => {
return (
<button
{...props}
class={cn(buttonVariants({ variant, buttonType }), props.class)}
class={cn(
buttonVariants({ variant, buttonType }),
props.class,
props.className,
)}
className={cn(
buttonVariants({ variant, buttonType }),
props.class,
props.className,
)}
>
{children}
</button>
Expand Down
9 changes: 7 additions & 2 deletions islands/layouts/legal/LegalHeader.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
import { cn } from '@/lib/utils.ts'
import { useShowHeader } from '@/hooks/utils/useShowHeader.ts'
import { lightBorderColor } from '@/components/utils/tailwinds.ts'
import {
basicTextColor,
lightBorderColor,
} from '@/components/utils/tailwinds.ts'
import TocMenuModalNav from '@/islands/layouts/posts/TocMenuModalNav.tsx'
import { MarkdownHeading } from '@/utils/markdown.ts'

type Props = {
title: string
headings: MarkdownHeading[]
}

export default function LegalHeader({ headings }: Props) {
export default function LegalHeader({ title, headings }: Props) {
const showHeader = useShowHeader()
return (
<>
Expand All @@ -25,6 +29,7 @@ export default function LegalHeader({ headings }: Props) {
)}
>
<div>
<p class={cn('text-xs', basicTextColor)}>{title}</p>
</div>
<div className='flex flex-grow' />
<div>
Expand Down
41 changes: 41 additions & 0 deletions islands/layouts/news/NewsHeader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { cn } from '@/lib/utils.ts'
import { useShowHeader } from '@/hooks/utils/useShowHeader.ts'
import {
basicTextColor,
lightBorderColor,
} from '@/components/utils/tailwinds.ts'
import TocMenuModalNav from '@/islands/layouts/posts/TocMenuModalNav.tsx'
import { MarkdownHeading } from '@/utils/markdown.ts'

type Props = {
title: string
headings: MarkdownHeading[]
}

export default function NewsHeader({ title, headings }: Props) {
const showHeader = useShowHeader()
return (
<>
<div
className={cn(
'sticky top-16 z-20',
'w-full -mt-8 mb-8 px-6 py-2',
'border-b border-t',
lightBorderColor,
'backdrop-blur-xl bg-opacity-70 dark:bg-opacity-20',
'transition-transform duration-300 ease-in-out',
'flex flex-row gap-10 md:hidden items-center justify-center',
showHeader.value ? '-translate-y-1' : '-translate-y-96',
)}
>
<div>
<p class={cn('text-xs', basicTextColor)}>{title}</p>
</div>
<div className='flex flex-grow' />
<div>
<TocMenuModalNav headings={headings} />
</div>
</div>
</>
)
}
98 changes: 98 additions & 0 deletions islands/rows/news/NewsIndexRow.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import { useSignal } from '@preact/signals'
import { newsPosts } from '@/generated/newsPostsIndex.ts'
import { useLocale } from '@/hooks/i18n/useLocale.ts'
import Link from '@/components/utils/Link.tsx'
import Button from '@/components/ui/Button.tsx'
import { useTranslation } from '@/hooks/i18n/useTranslation.ts'
import { cn } from '@/lib/utils.ts'
import {
basicTextColor,
lightBorderColor,
mainShardGradation,
} from '@/components/utils/tailwinds.ts'

type Props = {
defaultShowCounts: number
}

export default function NewsIndexRow({ defaultShowCounts }: Props) {
const visibleCount = useSignal(defaultShowCounts)
const t = useTranslation()
const { locale } = useLocale()

const allNews = useSignal(newsPosts[locale as keyof typeof newsPosts] || [])

const visibleNews = allNews.value.slice(0, visibleCount.value)

const loadMore = () => {
visibleCount.value += defaultShowCounts
}

return (
<div className='mx-auto max-w-7xl p-3 flex flex-col gap-12 my-24'>
<h3
class={cn(
'text-center text-3xl sm:text-4xl lg:text-5xl font-bold tracking-tight',
mainShardGradation,
)}
>
News
</h3>
<div className='grid grid-cols-1 gap-12 sm:grid-cols-2 lg:grid-cols-3'>
{visibleNews.map((news) => (
<div
key={news.path}
className={cn(
'flex flex-col',
'shadow-sm',
'border rounded-xl',
lightBorderColor,
)}
>
<Link href={news.path} className='hover:opacity-80'>
<img
src={news.thumbnail}
alt={news.title}
className='w-full rounded-t-xl'
/>
</Link>

<div className='border-t border-zinc-200 p-4 dark:border-zinc-500'>
<Link href={news.path} className='hover:opacity-80'>
<time
dateTime={news.date.replaceAll('-', '.')}
className='text-xs text-zinc-500 dark:text-zinc-400'
>
{news.date.replaceAll('-', '.')}
</time>
<h3
className={cn(
'mt-1 font-bold tracking-tight',
basicTextColor,
)}
>
{news.title}
</h3>
</Link>
</div>
<div className='flex-grow' />
<div className='px-4 pb-3 pt-1'>
<Link href={news.path}>
<Button variant='outline' class={cn('w-full')}>
{t('common.readThisArticle')}
</Button>
</Link>
</div>
</div>
))}
</div>
{visibleCount.value < allNews.value.length && (
<div className='flex justify-center'>
<Button variant='outline' onClick={loadMore}>
{t('common.loadMore')}
</Button>
</div>
)}
</div>
)
}
4 changes: 2 additions & 2 deletions routes/[locale]/(default)/(_rows)/CTARow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,15 @@ export default function CTARow({ state }: Props) {
<div>
<h2
className={cn(
'py-2 text-4xl font-extrabold tracking-tighter sm:text-5xl',
'p-2 text-4xl font-extrabold tracking-tighter sm:text-5xl',
mainShardGradation,
)}
>
{t('common.CTARow.title')}
</h2>
<p
className={cn(
'max-w-xl text-lg font-medium tracking-tight sm:text-xl',
'max-w-xl text-lg font-medium tracking-tight sm:text-xl px-2',
blightTextColor,
)}
>
Expand Down
2 changes: 1 addition & 1 deletion routes/[locale]/(default)/(_rows)/HomeHeroRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ export default function HomeHeroRow(
<div className='relative mx-auto flex z-10 max-w-3xl flex-col items-center gap-6 py-24 md:py-40 lg:max-w-4xl'>
<h1
className={cn(
'py-2 text-center text-4xl font-bold tracking-tighter sm:text-6xl lg:text-7xl',
'p-2 text-center text-4xl font-bold tracking-tighter sm:text-6xl lg:text-7xl',
mainShardGradation,
)}
>
Expand Down
2 changes: 2 additions & 0 deletions routes/[locale]/(default)/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { page } from 'fresh'
import HomeHeroRow from '@/routes/[locale]/(default)/(_rows)/HomeHeroRow.tsx'
import CTARow from '@/routes/[locale]/(default)/(_rows)/CTARow.tsx'
import ProductsSlideRow from '@/islands/rows/products/ProductsSlideRow.tsx'
import NewsIndexRow from '@/islands/rows/news/NewsIndexRow.tsx'

export const handler = define.handlers({
GET(ctx) {
Expand All @@ -20,6 +21,7 @@ export default define.page<typeof handler>(function Home(props) {
<HomeHeroRow state={props.state} />
<CTARow state={props.state} />
<ProductsSlideRow />
<NewsIndexRow defaultShowCounts={3} />
</>
)
})
2 changes: 1 addition & 1 deletion routes/[locale]/(default)/legal/[...slug].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export default define.page<typeof handler>(function LegalSlugPage(props) {

return (
<>
<LegalHeader headings={headings} />
<LegalHeader title={props.state.title || 'Legal'} headings={headings} />
<div className='mx-auto max-w-4xl p-3 md:py-8'>
<div className='grid grid-cols-1 gap-4 md:grid-cols-3'>
<div className='p-4 md:col-span-2 flex flex-col gap-12'>
Expand Down
112 changes: 112 additions & 0 deletions routes/[locale]/(default)/news/[...slug].tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import { define } from '@/utils/state.ts'
import { asset } from 'fresh/runtime'
import { HttpError, page } from 'fresh'
import { getPost } from '@/utils/posts.ts'
import { frontMatter, renderMarkdown } from '@/utils/markdown.ts'
import { cn } from '@/lib/utils.ts'
import {
lightTextColor,
mainShardGradation,
} from '@/components/utils/tailwinds.ts'
import Toc from '@/islands/posts/Toc.tsx'
import NewsHeader from '@/islands/layouts/news/NewsHeader.tsx'

const kind = 'news'

const pattern = new URLPattern({
pathname: `/:locale/${kind}/:year/:month/:date/:page*`,
})

export const handler = define.handlers({
async GET(ctx) {
const match = pattern.exec(ctx.url.href)
if (!match) {
throw new HttpError(404)
}

const post = await getPost({
kind,
slug: ctx.params.slug,
locale: ctx.params.locale,
})

if (!post) {
throw new HttpError(404)
}

const { body, attrs } = frontMatter<Record<string, string>>(post)

const slugParts = ctx.params.slug.split('/')
if (slugParts.length < 3) throw new HttpError(404)

const [year, month, day] = slugParts
const formattedDate = `${year}.${month}.${day}`

ctx.state.title = attrs.title
ctx.state.description = attrs.description
ctx.state.ogImage = new URL(asset(`${attrs.thumbnail}`), ctx.url).href

return page({
page: {
ctx,
body,
attrs,
date: formattedDate,
},
})
},
})

export default define.page<typeof handler>(function LegalSlugPage(props) {
const { html, headings } = renderMarkdown(props.data.page.body)

return (
<>
<NewsHeader title={props.state.title || 'News'} headings={headings} />
<div className='mx-auto max-w-4xl p-3 py-8 pt-24 text-center'>
<time
dateTime={props.data.page.date as string}
className={lightTextColor}
>
{props.data.page.date}
</time>
<h1
className={cn(
'py-6 text-4xl font-medium tracking-tight md:text-5xl',
mainShardGradation,
)}
>
{props.state.title}
</h1>
</div>
<div className='mx-auto max-w-5xl p-3 md:py-6'>
<img
src={asset(props.data.page.attrs.thumbnail)}
className='w-full rounded-xl'
/>
</div>
<div className='mx-auto max-w-4xl p-3 md:py-8'>
<div className='grid grid-cols-1 gap-4 md:grid-cols-3'>
<div className='p-4 md:col-span-2 flex flex-col gap-12'>
<div
class='prose prose-zinc dark:prose-invert'
dangerouslySetInnerHTML={{ __html: html }}
/>
</div>
<div className='max-h-full p-4 md:col-span-1'>
<div
className={cn(
'scrollbar-thin scrollbar-thumb-rounded-full scrollbar-track-rounded-full',
'scrollbar-track-white scrollbar-thumb-zinc-300 dark:scrollbar-track-zinc-950 dark:scrollbar-thumb-zinc-600',
'hidden md:sticky md:top-32 md:block',
'overflow-auto max-h-[calc(100vh-10rem)]',
)}
>
<Toc headings={headings} />
</div>
</div>
</div>
</div>
</>
)
})
25 changes: 25 additions & 0 deletions routes/[locale]/(default)/news/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { define } from '@/utils/state.ts'
import { asset } from 'fresh/runtime'
import { page } from 'fresh'
import CTARow from '@/routes/[locale]/(default)/(_rows)/CTARow.tsx'
import ProductsSlideRow from '@/islands/rows/products/ProductsSlideRow.tsx'
import NewsIndexRow from '@/islands/rows/news/NewsIndexRow.tsx'

export const handler = define.handlers({
GET(ctx) {
ctx.state.title = 'news.title'
ctx.state.description = 'metadata.description'
ctx.state.ogImage = new URL(asset('/ogp.jpg'), ctx.url).href
return page()
},
})

export default define.page<typeof handler>(function NewsIndex(props) {
return (
<>
<NewsIndexRow defaultShowCounts={12} />
<CTARow state={props.state} />
<ProductsSlideRow />
</>
)
})
Loading

0 comments on commit b133caa

Please sign in to comment.