use Next.js
This commit is contained in:
1 parent
cf94c66807
commit
20a00d5ab3
34 files changed
+7117
-1
No files matched your search
@@ -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
|
||||
@@ -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> … </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> … </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
|
||||
@@ -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
|
||||
{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
|
||||
Reference in new issue
Block a user