use Next.js

This commit is contained in:
polv committed 2020-12-17 23:15:28 +07:00
1 parent cf94c66807
commit 20a00d5ab3
34 files changed
+7117 -1

No files matched your search

+70
View File
@@ -0,0 +1,70 @@
import { THEME } from '~/assets/global'
import Link from 'next/link'
import { ReactNode } from 'react'
const Layout = ({ children }: { children: ReactNode }) => {
return (
<main>
<header>
<h1>{THEME.title}</h1>
<nav>
<ul>
<li>
<Link href="/">
<a>Home</a>
</Link>
</li>
{(THEME.nav || []).map((n) =>
n.to ? (
<Link href={n.to} key={n.name}>
<a>{n.name}</a>
</Link>
) : (
<a
href={n.href}
target="_blank"
rel="noopener noreferrer"
key={n.name}
>
{n.name}
</a>
)
)}
</ul>
<form>
<input name="q" type="search" placeholder="Search..." />
<div className="icon">🔎</div>
</form>
</nav>
</header>
{children}
<style jsx>{`
nav ul {
display: inline-block;
}
form {
float: right;
max-width: 300px;
position: relative;
}
form .icon {
position: absolute;
right: 5px;
top: 5px;
}
input {
padding-right: 1.5em;
}
`}</style>
</main>
)
}
export default Layout
+134
View File
@@ -0,0 +1,134 @@
import { IPost } from '~/server/db'
import Link from 'next/link'
import { useRouter } from 'next/router'
import { useEffect, useState } from 'react'
import PostTeaser from './PostTeaser'
const PostQuery = ({
defaults
}: {
defaults: {
posts: IPost[]
count: number
}
}) => {
const router = useRouter()
const page = parseInt(router.query.page as string) || 1
const makePageUrl = (p: number) => {
let pathname = router.pathname
.replace(/\/$/, '')
.replace(/\d+$/, '')
.replace(/\/$/, '')
if (pathname !== '/tag' && pathname !== '/blog') {
pathname = '/blog'
}
if (p > 1) {
pathname += '/' + p
}
if (router.query.q) {
pathname += `?q=${encodeURIComponent(router.query.q as string)}`
}
return pathname
}
const [posts, setPosts] = useState(defaults.posts)
const [count, setCount] = useState(defaults.count)
useEffect(() => {
if (router.query.q) {
const querystrings: string[] = []
for (const [k, v] of Object.entries({
q: router.query.q,
page: router.query.page,
tag: router.query.tag
})) {
if (v) {
querystrings.push(`${k}=${encodeURIComponent(v as string)}`)
}
}
fetch(`/api/q${querystrings.length ? `?${querystrings.join('&')}` : ''}`)
.then((r) => r.json())
.then(({ result, count }) => {
setPosts(result)
setCount(count)
})
} else {
setPosts(defaults.posts)
setCount(defaults.count)
}
}, [router.query.q, router.query.page, router.query.tag])
const totalPages = Math.ceil(count / 5)
return (
<article>
{posts.length ? (
posts.map((p) => <PostTeaser post={p} key={p.path} />)
) : (
<p>No posts found.</p>
)}
{count > 5 ? (
<nav>
<ul>
{page > 1 ? (
<li>
<Link href={makePageUrl(1)}>
<a aria-label="page 1">1</a>
</Link>
</li>
) : null}
{page > 3 ? (
<li>
<span> &hellip; </span>
</li>
) : null}
{page > 2 ? (
<li>
<Link href={makePageUrl(page - 1)}>
<a aria-label={`page ${page - 1}`}>{page - 1}</a>
</Link>
</li>
) : null}
<li>
<span>{page}</span>
</li>
{page < totalPages - 1 ? (
<li>
<Link href={makePageUrl(page + 1)}>
<a aria-label={`page ${page + 1}`}>{page + 1}</a>
</Link>
</li>
) : null}
{page < totalPages - 2 ? (
<li>
<span> &hellip; </span>
</li>
) : null}
{page < totalPages ? (
<li>
<Link href={makePageUrl(totalPages)}>
<a aria-label={`page ${totalPages}`}>{totalPages}</a>
</Link>
</li>
) : null}
</ul>
</nav>
) : null}
</article>
)
}
export default PostQuery
+44
View File
@@ -0,0 +1,44 @@
import { THEME } from '~/assets/global'
import { formatDate } from '~/assets/util'
import { IPostTeaser } from '~/server/db'
import Link from 'next/link'
const PostTeaser = ({ post }: { post: IPostTeaser }) => {
return (
<section>
<h2>
<Link href={`/post/${post.path}`}>
<a>{post.title}</a>
</Link>
</h2>
<small>
<span>
by&nbsp;
{THEME.author.url ? (
<a
href={THEME.author.url}
target="_blank"
rel="noopener noreferrer"
>
{THEME.author.name}
</a>
) : (
<span>{THEME.author.name}</span>
)}
</span>
{post.date ? (
<span style={{ float: 'right' }}>{formatDate(post.date)}</span>
) : null}
</small>
<article
style={{ marginTop: '2rem' }}
dangerouslySetInnerHTML={{ __html: post.excerptHtml }}
></article>
</section>
)
}
export default PostTeaser