From ba90f14f9b07c786a76aaa5ccb9137624f68209d Mon Sep 17 00:00:00 2001 From: iCrawl Date: Sat, 8 Oct 2022 15:44:00 +0200 Subject: [PATCH] feat: auto-link headings --- packages/guide/astro.config.ts | 62 ++++++++++++++++++- packages/guide/package.json | 5 ++ .../guide/src/components/SidebarLayout.astro | 4 +- packages/ui/unocss.config.ts | 16 +++++ yarn.lock | 22 ++++++- 5 files changed, 105 insertions(+), 4 deletions(-) diff --git a/packages/guide/astro.config.ts b/packages/guide/astro.config.ts index c58ddfcc1..a976a634d 100644 --- a/packages/guide/astro.config.ts +++ b/packages/guide/astro.config.ts @@ -4,9 +4,42 @@ import mdx from '@astrojs/mdx'; import react from '@astrojs/react'; import { remarkCodeHike } from '@code-hike/mdx'; import { defineConfig } from 'astro/config'; +import { toString } from 'hast-util-to-string'; +import { h } from 'hastscript'; +import { escape } from 'html-escaper'; +import rehypeAutolinkHeadings from 'rehype-autolink-headings'; +import rehypeSlug from 'rehype-slug'; import shikiThemeDarkPlus from 'shiki/themes/dark-plus.json' assert { type: 'json' }; import Unocss from 'unocss/astro'; +const LinkIcon = h( + 'svg', + { + width: '1rem', + height: '1rem', + viewBox: '0 0 24 24', + fill: 'none', + stroke: 'currentColor', + strokeWidth: '2', + strokeLinecap: 'round', + strokeLinejoin: 'round', + }, + h('path', { + // eslint-disable-next-line id-length + d: 'M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71', + }), + h('path', { + // eslint-disable-next-line id-length + d: 'M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71', + }), +); + +const createSROnlyLabel = (text: string) => { + const node = h('span.sr-only', `Section titled ${escape(text)}`); + node.properties!['is:raw'] = true; + return node; +}; + export default defineConfig({ integrations: [ react(), @@ -20,7 +53,34 @@ export default defineConfig({ ], markdown: { remarkPlugins: [[remarkCodeHike, { autoImport: false, theme: shikiThemeDarkPlus, lineNumbers: true }]], - rehypePlugins: [], + rehypePlugins: [ + rehypeSlug, + [ + rehypeAutolinkHeadings, + { + properties: { + class: + 'relative inline-flex w-6 h-6 place-items-center place-content-center outline-0 text-black dark:text-white ml-2', + }, + behavior: 'after', + group: ({ tagName }) => + h('div', { + class: `[&>*]:inline-block [&>h1]:m-0 [&>h2]:m-0 [&>h3]:m-0 [&>h4]:m-0 level-${tagName}`, + tabIndex: -1, + }), + content: (heading) => [ + h( + `span.anchor-icon`, + { + ariaHidden: 'true', + }, + LinkIcon, + ), + createSROnlyLabel(toString(heading)), + ], + }, + ], + ], extendDefaultPlugins: true, syntaxHighlight: false, }, diff --git a/packages/guide/package.json b/packages/guide/package.json index 660252541..546b71626 100644 --- a/packages/guide/package.json +++ b/packages/guide/package.json @@ -72,9 +72,14 @@ "eslint": "^8.24.0", "eslint-config-neon": "^0.1.35", "happy-dom": "^7.4.0", + "hast-util-to-string": "^2.0.0", + "hastscript": "^7.0.2", + "html-escaper": "^3.0.3", "prettier": "^2.7.1", "prettier-plugin-astro": "^0.5.5", "prettier-plugin-tailwindcss": "^0.1.13", + "rehype-autolink-headings": "^6.1.1", + "rehype-slug": "^5.0.1", "typescript": "^4.8.4", "unocss": "^0.45.26", "vercel": "^28.4.8", diff --git a/packages/guide/src/components/SidebarLayout.astro b/packages/guide/src/components/SidebarLayout.astro index db3848b77..42b0499c2 100644 --- a/packages/guide/src/components/SidebarLayout.astro +++ b/packages/guide/src/components/SidebarLayout.astro @@ -13,7 +13,7 @@ const { headings } = Astro.props;