forked from timlrx/tailwind-nextjs-starter-blog
-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathListLayout.tsx
152 lines (145 loc) · 5.3 KB
/
ListLayout.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
'use client'
import { useState } from 'react'
import { usePathname } from 'next/navigation'
import { formatDate } from 'pliny/utils/formatDate'
import { CoreContent } from 'pliny/utils/contentlayer'
import type { Blog } from 'contentlayer/generated'
import Link from '@/components/Link'
import Tag from '@/components/Tag'
import siteMetadata from '@/data/siteMetadata'
interface PaginationProps {
totalPages: number
currentPage: number
}
interface ListLayoutProps {
posts: CoreContent<Blog>[]
title: string
initialDisplayPosts?: CoreContent<Blog>[]
pagination?: PaginationProps
}
function Pagination({ totalPages, currentPage }: PaginationProps) {
const pathname = usePathname()
const basePath = pathname.split('/')[1]
const prevPage = currentPage - 1 > 0
const nextPage = currentPage + 1 <= totalPages
return (
<div className="space-y-2 pb-8 pt-6 md:space-y-5">
<nav className="flex justify-between">
{!prevPage && (
<button className="cursor-auto disabled:opacity-50" disabled={!prevPage}>
Previous
</button>
)}
{prevPage && (
<Link
href={currentPage - 1 === 1 ? `/${basePath}/` : `/${basePath}/page/${currentPage - 1}`}
rel="prev"
>
Previous
</Link>
)}
<span>
{currentPage} of {totalPages}
</span>
{!nextPage && (
<button className="cursor-auto disabled:opacity-50" disabled={!nextPage}>
Next
</button>
)}
{nextPage && (
<Link href={`/${basePath}/page/${currentPage + 1}`} rel="next">
Next
</Link>
)}
</nav>
</div>
)
}
export default function ListLayout({
posts,
title,
initialDisplayPosts = [],
pagination,
}: ListLayoutProps) {
const [searchValue, setSearchValue] = useState('')
const filteredBlogPosts = posts.filter((post) => {
const searchContent = post.title + post.summary + post.tags?.join(' ')
return searchContent.toLowerCase().includes(searchValue.toLowerCase())
})
// If initialDisplayPosts exist, display it if no searchValue is specified
const displayPosts =
initialDisplayPosts.length > 0 && !searchValue ? initialDisplayPosts : filteredBlogPosts
return (
<>
<div className="divide-y divide-gray-200 dark:divide-gray-700">
<div className="space-y-2 pb-8 pt-6 md:space-y-5">
<h1 className="text-3xl font-extrabold leading-9 tracking-tight text-gray-900 dark:text-gray-100 sm:text-4xl sm:leading-10 md:text-6xl md:leading-14">
{title}
</h1>
<div className="relative max-w-lg">
<label>
<span className="sr-only">Search articles</span>
<input
aria-label="Search articles"
type="text"
onChange={(e) => setSearchValue(e.target.value)}
placeholder="Search articles"
className="block w-full rounded-md border border-gray-300 bg-white px-4 py-2 text-gray-900 focus:border-primary-500 focus:ring-primary-500 dark:border-gray-900 dark:bg-gray-800 dark:text-gray-100"
/>
</label>
<svg
className="absolute right-3 top-3 h-5 w-5 text-gray-400 dark:text-gray-300"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"
/>
</svg>
</div>
</div>
<ul>
{!filteredBlogPosts.length && 'No posts found.'}
{displayPosts.map((post) => {
const { path, date, title, summary, tags } = post
return (
<li key={path} className="py-4">
<article className="space-y-2 xl:grid xl:grid-cols-4 xl:items-baseline xl:space-y-0">
<dl>
<dt className="sr-only">Published on</dt>
<dd className="text-base font-medium leading-6 text-gray-500 dark:text-gray-400">
<time dateTime={date}>{formatDate(date, siteMetadata.locale)}</time>
</dd>
</dl>
<div className="space-y-3 xl:col-span-3">
<div>
<h3 className="text-2xl font-bold leading-8 tracking-tight">
<Link href={`/${path}`} className="text-gray-900 dark:text-gray-100">
{title}
</Link>
</h3>
<div className="flex flex-wrap">
{tags?.map((tag) => <Tag key={tag} text={tag} />)}
</div>
</div>
<div className="prose max-w-none text-gray-500 dark:text-gray-400">
{summary}
</div>
</div>
</article>
</li>
)
})}
</ul>
</div>
{pagination && pagination.totalPages > 1 && !searchValue && (
<Pagination currentPage={pagination.currentPage} totalPages={pagination.totalPages} />
)}
</>
)
}