update,
This commit is contained in:
61
aastock/1/blog/app/components/footer.tsx
Normal file
61
aastock/1/blog/app/components/footer.tsx
Normal file
@@ -0,0 +1,61 @@
|
||||
function ArrowIcon() {
|
||||
return (
|
||||
<svg
|
||||
width="12"
|
||||
height="12"
|
||||
viewBox="0 0 12 12"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M2.07102 11.3494L0.963068 10.2415L9.2017 1.98864H2.83807L2.85227 0.454545H11.8438V9.46023H10.2955L10.3097 3.09659L2.07102 11.3494Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export default function Footer() {
|
||||
return (
|
||||
<footer className="mb-16">
|
||||
<ul className="font-sm mt-8 flex flex-col space-x-0 space-y-2 text-neutral-600 md:flex-row md:space-x-4 md:space-y-0 dark:text-neutral-300">
|
||||
<li>
|
||||
<a
|
||||
className="flex items-center transition-all hover:text-neutral-800 dark:hover:text-neutral-100"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
href="/rss"
|
||||
>
|
||||
<ArrowIcon />
|
||||
<p className="ml-2 h-7">rss</p>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
className="flex items-center transition-all hover:text-neutral-800 dark:hover:text-neutral-100"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
href="https://github.com/vercel/next.js"
|
||||
>
|
||||
<ArrowIcon />
|
||||
<p className="ml-2 h-7">github</p>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
className="flex items-center transition-all hover:text-neutral-800 dark:hover:text-neutral-100"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
href="https://vercel.com/templates/next.js/portfolio-starter-kit"
|
||||
>
|
||||
<ArrowIcon />
|
||||
<p className="ml-2 h-7">view source</p>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<p className="mt-8 text-neutral-600 dark:text-neutral-300">
|
||||
© {new Date().getFullYear()} MIT Licensed
|
||||
</p>
|
||||
</footer>
|
||||
)
|
||||
}
|
109
aastock/1/blog/app/components/mdx.tsx
Normal file
109
aastock/1/blog/app/components/mdx.tsx
Normal file
@@ -0,0 +1,109 @@
|
||||
import Link from 'next/link'
|
||||
import Image from 'next/image'
|
||||
import { MDXRemote } from 'next-mdx-remote/rsc'
|
||||
import { highlight } from 'sugar-high'
|
||||
import React from 'react'
|
||||
|
||||
function Table({ data }) {
|
||||
let headers = data.headers.map((header, index) => (
|
||||
<th key={index}>{header}</th>
|
||||
))
|
||||
let rows = data.rows.map((row, index) => (
|
||||
<tr key={index}>
|
||||
{row.map((cell, cellIndex) => (
|
||||
<td key={cellIndex}>{cell}</td>
|
||||
))}
|
||||
</tr>
|
||||
))
|
||||
|
||||
return (
|
||||
<table>
|
||||
<thead>
|
||||
<tr>{headers}</tr>
|
||||
</thead>
|
||||
<tbody>{rows}</tbody>
|
||||
</table>
|
||||
)
|
||||
}
|
||||
|
||||
function CustomLink(props) {
|
||||
let href = props.href
|
||||
|
||||
if (href.startsWith('/')) {
|
||||
return (
|
||||
<Link href={href} {...props}>
|
||||
{props.children}
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
|
||||
if (href.startsWith('#')) {
|
||||
return <a {...props} />
|
||||
}
|
||||
|
||||
return <a target="_blank" rel="noopener noreferrer" {...props} />
|
||||
}
|
||||
|
||||
function RoundedImage(props) {
|
||||
return <Image alt={props.alt} className="rounded-lg" {...props} />
|
||||
}
|
||||
|
||||
function Code({ children, ...props }) {
|
||||
let codeHTML = highlight(children)
|
||||
return <code dangerouslySetInnerHTML={{ __html: codeHTML }} {...props} />
|
||||
}
|
||||
|
||||
function slugify(str) {
|
||||
return str
|
||||
.toString()
|
||||
.toLowerCase()
|
||||
.trim() // Remove whitespace from both ends of a string
|
||||
.replace(/\s+/g, '-') // Replace spaces with -
|
||||
.replace(/&/g, '-and-') // Replace & with 'and'
|
||||
.replace(/[^\w\-]+/g, '') // Remove all non-word characters except for -
|
||||
.replace(/\-\-+/g, '-') // Replace multiple - with single -
|
||||
}
|
||||
|
||||
function createHeading(level) {
|
||||
const Heading = ({ children }) => {
|
||||
let slug = slugify(children)
|
||||
return React.createElement(
|
||||
`h${level}`,
|
||||
{ id: slug },
|
||||
[
|
||||
React.createElement('a', {
|
||||
href: `#${slug}`,
|
||||
key: `link-${slug}`,
|
||||
className: 'anchor',
|
||||
}),
|
||||
],
|
||||
children
|
||||
)
|
||||
}
|
||||
|
||||
Heading.displayName = `Heading${level}`
|
||||
|
||||
return Heading
|
||||
}
|
||||
|
||||
let components = {
|
||||
h1: createHeading(1),
|
||||
h2: createHeading(2),
|
||||
h3: createHeading(3),
|
||||
h4: createHeading(4),
|
||||
h5: createHeading(5),
|
||||
h6: createHeading(6),
|
||||
Image: RoundedImage,
|
||||
a: CustomLink,
|
||||
code: Code,
|
||||
Table,
|
||||
}
|
||||
|
||||
export function CustomMDX(props) {
|
||||
return (
|
||||
<MDXRemote
|
||||
{...props}
|
||||
components={{ ...components, ...(props.components || {}) }}
|
||||
/>
|
||||
)
|
||||
}
|
40
aastock/1/blog/app/components/nav.tsx
Normal file
40
aastock/1/blog/app/components/nav.tsx
Normal file
@@ -0,0 +1,40 @@
|
||||
import Link from 'next/link'
|
||||
|
||||
const navItems = {
|
||||
'/': {
|
||||
name: 'home',
|
||||
},
|
||||
'/blog': {
|
||||
name: 'blog',
|
||||
},
|
||||
'https://vercel.com/templates/next.js/portfolio-starter-kit': {
|
||||
name: 'deploy',
|
||||
},
|
||||
}
|
||||
|
||||
export function Navbar() {
|
||||
return (
|
||||
<aside className="-ml-[8px] mb-16 tracking-tight">
|
||||
<div className="lg:sticky lg:top-20">
|
||||
<nav
|
||||
className="flex flex-row items-start relative px-0 pb-0 fade md:overflow-auto scroll-pr-6 md:relative"
|
||||
id="nav"
|
||||
>
|
||||
<div className="flex flex-row space-x-0 pr-10">
|
||||
{Object.entries(navItems).map(([path, { name }]) => {
|
||||
return (
|
||||
<Link
|
||||
key={path}
|
||||
href={path}
|
||||
className="transition-all hover:text-neutral-800 dark:hover:text-neutral-200 flex align-middle relative py-1 px-2 m-1"
|
||||
>
|
||||
{name}
|
||||
</Link>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
</aside>
|
||||
)
|
||||
}
|
36
aastock/1/blog/app/components/posts.tsx
Normal file
36
aastock/1/blog/app/components/posts.tsx
Normal file
@@ -0,0 +1,36 @@
|
||||
import Link from 'next/link'
|
||||
import { formatDate, getBlogPosts } from 'app/blog/utils'
|
||||
|
||||
export function BlogPosts() {
|
||||
let allBlogs = getBlogPosts()
|
||||
|
||||
return (
|
||||
<div>
|
||||
{allBlogs
|
||||
.sort((a, b) => {
|
||||
if (
|
||||
new Date(a.metadata.publishedAt) > new Date(b.metadata.publishedAt)
|
||||
) {
|
||||
return -1
|
||||
}
|
||||
return 1
|
||||
})
|
||||
.map((post) => (
|
||||
<Link
|
||||
key={post.slug}
|
||||
className="flex flex-col space-y-1 mb-4"
|
||||
href={`/blog/${post.slug}`}
|
||||
>
|
||||
<div className="w-full flex flex-col md:flex-row space-x-0 md:space-x-2">
|
||||
<p className="text-neutral-600 dark:text-neutral-400 w-[100px] tabular-nums">
|
||||
{formatDate(post.metadata.publishedAt, false)}
|
||||
</p>
|
||||
<p className="text-neutral-900 dark:text-neutral-100 tracking-tight">
|
||||
{post.metadata.title}
|
||||
</p>
|
||||
</div>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
Reference in New Issue
Block a user