mirror of
https://github.com/kremalicious/blog.git
synced 2024-06-28 16:48:00 +02:00
add table of contents
This commit is contained in:
parent
006447355b
commit
735a48c1c1
|
@ -1,18 +1,19 @@
|
|||
import { defineConfig } from 'astro/config'
|
||||
import { remarkLeadParagraph } from './src/lib/remark-lead-paragraph.mjs'
|
||||
import remarkLeadParagraph from './src/lib/remark-lead-paragraph.mjs'
|
||||
import remarkToc from './src/lib/remark-toc.mjs'
|
||||
import react from '@astrojs/react'
|
||||
|
||||
// https://astro.build/config
|
||||
export default defineConfig({
|
||||
site: 'https://kremalicious.com',
|
||||
markdown: {
|
||||
remarkPlugins: [remarkLeadParagraph, remarkToc],
|
||||
shikiConfig: {
|
||||
// https://github.com/shikijs/shiki/blob/main/docs/themes.md
|
||||
theme: 'github-dark-dimmed',
|
||||
langs: [],
|
||||
wrap: true
|
||||
},
|
||||
remarkPlugins: [remarkLeadParagraph]
|
||||
}
|
||||
},
|
||||
integrations: [react()]
|
||||
})
|
||||
|
|
1897
package-lock.json
generated
1897
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
|
@ -64,9 +64,12 @@
|
|||
"eslint-plugin-prettier": "^5.0.0",
|
||||
"eslint-plugin-react": "^7.33.2",
|
||||
"eslint-plugin-testing-library": "^6.0.1",
|
||||
"hast-util-to-html": "^9.0.0",
|
||||
"jest": "^29.6.4",
|
||||
"jest-environment-jsdom": "^29.6.4",
|
||||
"markdownlint-cli": "^0.35.0",
|
||||
"mdast-util-to-hast": "^13.0.2",
|
||||
"mdast-util-toc": "^7.0.0",
|
||||
"node-iptc": "^1.0.5",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"ora": "^7.0.1",
|
||||
|
|
17
src/components/Toc/Toc.astro
Normal file
17
src/components/Toc/Toc.astro
Normal file
|
@ -0,0 +1,17 @@
|
|||
---
|
||||
import styles from './toc.module.css'
|
||||
|
||||
type Props = {
|
||||
tableOfContents: string
|
||||
}
|
||||
|
||||
const { tableOfContents } = Astro.props
|
||||
---
|
||||
|
||||
<style></style>
|
||||
|
||||
<nav
|
||||
aria-label="Table of Contents"
|
||||
class={styles.toc}
|
||||
set:html={tableOfContents}
|
||||
/>
|
|
@ -5,7 +5,8 @@
|
|||
margin-bottom: calc(var(--spacer) * var(--line-height));
|
||||
}
|
||||
|
||||
.toc ul {
|
||||
.toc ul,
|
||||
.toc p {
|
||||
margin: 0;
|
||||
}
|
||||
|
|
@ -6,13 +6,15 @@ import Actions from './Actions.astro'
|
|||
import styles from './index.module.css'
|
||||
import Meta from './Meta.astro'
|
||||
import Picture from '@components/core/Picture.astro'
|
||||
import Toc from '@components/Toc/Toc.astro'
|
||||
|
||||
type Props = CollectionEntry<'articles' | 'links' | 'photos'> & {
|
||||
lead?: string // comes in through remark plugin as html
|
||||
tableOfContents?: string // comes in through remark plugin as html
|
||||
}
|
||||
|
||||
const { data, collection, lead } = Astro.props
|
||||
const { title, date, updated, image, linkurl } = data
|
||||
const { data, collection, lead, tableOfContents } = Astro.props
|
||||
const { title, date, updated, image, linkurl, toc } = data
|
||||
---
|
||||
|
||||
<LayoutBase title={title}>
|
||||
|
@ -39,6 +41,8 @@ const { title, date, updated, image, linkurl } = data
|
|||
)
|
||||
}
|
||||
|
||||
{toc && tableOfContents && <Toc tableOfContents={tableOfContents} />}
|
||||
|
||||
<slot />
|
||||
|
||||
<Meta post={Astro.props} />
|
||||
|
|
|
@ -22,7 +22,8 @@ const schemaShared = {
|
|||
redirect_from: z.array(z.string()).optional(),
|
||||
author: z.string().optional(),
|
||||
featured: z.boolean().optional(),
|
||||
style: z.string().optional()
|
||||
style: z.string().optional(),
|
||||
toc: z.boolean().optional()
|
||||
}
|
||||
|
||||
export const schemaArticles = (image: ImageFunction) =>
|
||||
|
@ -35,7 +36,6 @@ export const schemaArticles = (image: ImageFunction) =>
|
|||
// })
|
||||
.optional(),
|
||||
download: z.string().optional(),
|
||||
toc: z.boolean().optional(),
|
||||
changelog: z.string().optional()
|
||||
})
|
||||
.strict()
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
import React, { ReactElement } from 'react'
|
||||
import styles from './Toc.module.css'
|
||||
|
||||
const PostToc = ({
|
||||
tableOfContents
|
||||
}: {
|
||||
tableOfContents: string
|
||||
}): ReactElement => {
|
||||
return (
|
||||
<nav
|
||||
aria-label="Table of Contents"
|
||||
className={styles.toc}
|
||||
dangerouslySetInnerHTML={{ __html: tableOfContents }}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export default PostToc
|
|
@ -1,4 +1,4 @@
|
|||
export function remarkLeadParagraph() {
|
||||
export default function remarkLeadParagraph() {
|
||||
return function (tree, file) {
|
||||
// run only on articles
|
||||
if (!file.history[0].includes('articles')) return
|
||||
|
|
28
src/lib/remark-toc.mjs
Normal file
28
src/lib/remark-toc.mjs
Normal file
|
@ -0,0 +1,28 @@
|
|||
//
|
||||
// Extract headings from markdown and add them as HTML to the frontmatter
|
||||
// Similiar to https://github.com/remarkjs/remark-toc
|
||||
//
|
||||
import { toc } from 'mdast-util-toc'
|
||||
import { toHast } from 'mdast-util-to-hast'
|
||||
import { toHtml } from 'hast-util-to-html'
|
||||
|
||||
export default function remarkToc() {
|
||||
return (tree, file) => {
|
||||
const result = toc(tree, { maxDepth: 3 })
|
||||
|
||||
if (
|
||||
result.endIndex === null ||
|
||||
result.index === null ||
|
||||
result.index === -1 ||
|
||||
!result.map
|
||||
) {
|
||||
return
|
||||
}
|
||||
|
||||
const hast = toHast(result.map)
|
||||
const html = toHtml(hast)
|
||||
|
||||
// Add toc to frontmatter
|
||||
file.data.astro.frontmatter.tableOfContents = html.toString()
|
||||
}
|
||||
}
|
|
@ -24,6 +24,10 @@ const { entry } = Astro.props
|
|||
const { Content, remarkPluginFrontmatter } = await entry.render()
|
||||
---
|
||||
|
||||
<LayoutPost {...entry} lead={remarkPluginFrontmatter.lead}>
|
||||
<LayoutPost
|
||||
{...entry}
|
||||
lead={remarkPluginFrontmatter.lead}
|
||||
tableOfContents={remarkPluginFrontmatter.tableOfContents}
|
||||
>
|
||||
<Content />
|
||||
</LayoutPost>
|
||||
|
|
|
@ -1,14 +1,20 @@
|
|||
import { getCollection } from 'astro:content'
|
||||
import { SITE_TITLE, SITE_DESCRIPTION } from '../consts'
|
||||
import { loadAndFormatCollection } from '@lib/astro'
|
||||
import config from '@config/blog.config.mjs'
|
||||
|
||||
const { siteTitle, siteDescription } = config
|
||||
|
||||
export async function get(context) {
|
||||
const posts = await getCollection('blog')
|
||||
const articles = await loadAndFormatCollection('articles')
|
||||
const links = await loadAndFormatCollection('links')
|
||||
const photos = await loadAndFormatCollection('photos')
|
||||
const allPosts = [...articles, ...links, ...photos]
|
||||
|
||||
return {
|
||||
body: JSON.stringify({
|
||||
title: SITE_TITLE,
|
||||
description: SITE_DESCRIPTION,
|
||||
title: siteTitle,
|
||||
description: siteDescription,
|
||||
site: context.site,
|
||||
items: posts.map((post) => ({
|
||||
items: allPosts.map((post) => ({
|
||||
...post.data,
|
||||
link: `/blog/${post.slug}/`
|
||||
}))
|
||||
|
|
|
@ -1,16 +1,24 @@
|
|||
import rss from '@astrojs/rss'
|
||||
import { getCollection } from 'astro:content'
|
||||
import { SITE_TITLE, SITE_DESCRIPTION } from '../consts'
|
||||
import config from '@config/blog.config.mjs'
|
||||
import { loadAndFormatCollection } from '@lib/astro'
|
||||
|
||||
const { siteTitle, siteDescription } = config
|
||||
|
||||
export async function get(context) {
|
||||
const posts = await getCollection('blog')
|
||||
const articles = await loadAndFormatCollection('articles')
|
||||
const links = await loadAndFormatCollection('links')
|
||||
const photos = await loadAndFormatCollection('photos')
|
||||
const allPosts = [...articles, ...links, ...photos]
|
||||
|
||||
return rss({
|
||||
title: SITE_TITLE,
|
||||
description: SITE_DESCRIPTION,
|
||||
title: siteTitle,
|
||||
description: siteDescription,
|
||||
site: context.site,
|
||||
items: posts.map((post) => ({
|
||||
...post.data,
|
||||
link: `/blog/${post.slug}/`
|
||||
items: allPosts.map((post) => ({
|
||||
title: post.data.title,
|
||||
pubDate: post.data.date as Date,
|
||||
link: `/blog/${post.slug}/`,
|
||||
content: post.body
|
||||
}))
|
||||
})
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user