upstream #1
| @@ -1,4 +1,4 @@ | ||||
| import ListLayout from '@/layouts/ListLayout' | ||||
| import ListLayout from '@/layouts/ListLayoutWithTags' | ||||
| import { allCoreContent, sortPosts } from 'pliny/utils/contentlayer' | ||||
| import { allBlogs } from 'contentlayer/generated' | ||||
| import { genPageMetadata } from 'app/seo' | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| import ListLayout from '@/layouts/ListLayout' | ||||
| import ListLayout from '@/layouts/ListLayoutWithTags' | ||||
| import { allCoreContent, sortPosts } from 'pliny/utils/contentlayer' | ||||
| import { allBlogs } from 'contentlayer/generated' | ||||
|  | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| import { slug } from 'github-slugger' | ||||
| import { allCoreContent } from 'pliny/utils/contentlayer' | ||||
| import siteMetadata from '@/data/siteMetadata' | ||||
| import ListLayout from '@/layouts/ListLayout' | ||||
| import ListLayout from '@/layouts/ListLayoutWithTags' | ||||
| import { allBlogs } from 'contentlayer/generated' | ||||
| import tagData from 'app/tag-data.json' | ||||
| import { genPageMetadata } from 'app/seo' | ||||
|   | ||||
							
								
								
									
										163
									
								
								layouts/ListLayoutWithTags.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										163
									
								
								layouts/ListLayoutWithTags.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,163 @@ | ||||
| /* eslint-disable jsx-a11y/anchor-is-valid */ | ||||
| 'use client' | ||||
|  | ||||
| import { usePathname } from 'next/navigation' | ||||
| import { slug } from 'github-slugger' | ||||
| 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' | ||||
| import tagData from 'app/tag-data.json' | ||||
|  | ||||
| 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 ListLayoutWithTags({ | ||||
|   posts, | ||||
|   title, | ||||
|   initialDisplayPosts = [], | ||||
|   pagination, | ||||
| }: ListLayoutProps) { | ||||
|   const pathname = usePathname() | ||||
|   const tagCounts = tagData as Record<string, number> | ||||
|   const tagKeys = Object.keys(tagCounts) | ||||
|   const sortedTags = tagKeys.sort((a, b) => tagCounts[b] - tagCounts[a]) | ||||
|  | ||||
|   const displayPosts = initialDisplayPosts.length > 0 ? initialDisplayPosts : posts | ||||
|  | ||||
|   return ( | ||||
|     <> | ||||
|       <div> | ||||
|         <div className="pb-6 pt-6"> | ||||
|           <h1 className="sm:hidden 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> | ||||
|         <div className="flex sm:space-x-24"> | ||||
|           <div className="hidden h-full sm:flex flex-wrap bg-gray-50 dark:bg-gray-900/70 shadow-md pt-5 dark:shadow-gray-800/40 rounded min-w-[260px] max-w-[260px]"> | ||||
|             <div className="py-4 px-6"> | ||||
|               {pathname.startsWith('/blog') ? ( | ||||
|                 <h3 className="text-primary-500 font-bold uppercase">All Posts</h3> | ||||
|               ) : ( | ||||
|                 <Link | ||||
|                   href={`/blog`} | ||||
|                   className="font-bold uppercase text-gray-700 dark:text-gray-300 hover:text-primary-500 dark:hover:text-primary-500" | ||||
|                 > | ||||
|                   All Posts | ||||
|                 </Link> | ||||
|               )} | ||||
|               <ul> | ||||
|                 {sortedTags.map((t) => { | ||||
|                   return ( | ||||
|                     <li key={t} className="my-3"> | ||||
|                       {pathname.split('/tags/')[1] === slug(t) ? ( | ||||
|                         <h3 className="inline py-2 px-3 uppercase text-sm font-bold text-primary-500"> | ||||
|                           {`${t} (${tagCounts[t]})`} | ||||
|                         </h3> | ||||
|                       ) : ( | ||||
|                         <Link | ||||
|                           href={`/tags/${slug(t)}`} | ||||
|                           className="py-2 px-3 uppercase text-sm font-medium text-gray-500 dark:text-gray-300 hover:text-primary-500 dark:hover:text-primary-500" | ||||
|                           aria-label={`View posts tagged ${t}`} | ||||
|                         > | ||||
|                           {`${t} (${tagCounts[t]})`} | ||||
|                         </Link> | ||||
|                       )} | ||||
|                     </li> | ||||
|                   ) | ||||
|                 })} | ||||
|               </ul> | ||||
|             </div> | ||||
|           </div> | ||||
|           <div> | ||||
|             <ul> | ||||
|               {displayPosts.map((post) => { | ||||
|                 const { path, date, title, summary, tags } = post | ||||
|                 return ( | ||||
|                   <li key={path} className="py-5"> | ||||
|                     <article className="space-y-2 flex flex-col 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"> | ||||
|                         <div> | ||||
|                           <h2 className="text-2xl font-bold leading-8 tracking-tight"> | ||||
|                             <Link href={`/${path}`} className="text-gray-900 dark:text-gray-100"> | ||||
|                               {title} | ||||
|                             </Link> | ||||
|                           </h2> | ||||
|                           <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> | ||||
|             {pagination && pagination.totalPages > 1 && ( | ||||
|               <Pagination currentPage={pagination.currentPage} totalPages={pagination.totalPages} /> | ||||
|             )} | ||||
|           </div> | ||||
|         </div> | ||||
|       </div> | ||||
|     </> | ||||
|   ) | ||||
| } | ||||
		Reference in New Issue
	
	Block a user