From 8d212e48593796614fa7c00d97f34bee9def09dd Mon Sep 17 00:00:00 2001 From: iCrawl Date: Sat, 29 Apr 2023 20:05:22 +0200 Subject: [PATCH] feat(guide): anchor links --- apps/guide/contentlayer.config.ts | 103 ++++++++++++++---------------- apps/guide/package.json | 3 +- apps/guide/src/components/H1.tsx | 9 +++ apps/guide/src/components/Mdx.tsx | 12 +++- unocss.config.ts | 2 + yarn.lock | 8 +++ 6 files changed, 80 insertions(+), 57 deletions(-) create mode 100644 apps/guide/src/components/H1.tsx diff --git a/apps/guide/contentlayer.config.ts b/apps/guide/contentlayer.config.ts index 93862f06a..6890967d9 100644 --- a/apps/guide/contentlayer.config.ts +++ b/apps/guide/contentlayer.config.ts @@ -1,9 +1,9 @@ import { remarkCodeHike } from '@code-hike/mdx'; import { defineDocumentType, makeSource } from 'contentlayer/source-files'; -// import { type Node, toString } from 'hast-util-to-string'; -// import { h } from 'hastscript'; -// import { escape } from 'html-escaper'; -// import rehypeAutolinkHeadings from 'rehype-autolink-headings'; +import { type Node, 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 remarkGfm from 'remark-gfm'; import codeHikeThemeDarkPlus from './src/styles/code-hike-theme-dark-plus.json'; @@ -36,33 +36,31 @@ export const Content = defineDocumentType(() => ({ }, })); -// 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 LinkIcon = h( + 'svg', + { + width: '1.25rem', + height: '1.25rem', + 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: any) => { -// const node = h('span.sr-only', `Section titled ${escape(text)}`); -// node.properties!['is:raw'] = true; -// return node; -// }; +const createSROnlyLabel = (text: any) => { + return h('span', { class: 'sr-only' }, `Section titled ${escape(text)}`); +}; export default makeSource({ contentDirPath: 'src/content', @@ -71,31 +69,26 @@ export default makeSource({ remarkPlugins: [remarkGfm, [remarkCodeHike, { theme: codeHikeThemeDarkPlus, lineNumbers: true }]], rehypePlugins: [ rehypeSlug, - // [ - // rehypeAutolinkHeadings, - // { - // properties: { - // class: - // 'relative inline-flex w-6 h-6 place-items-center place-content-center outline-none text-black dark:text-white ml-2', - // }, - // behavior: 'after', - // group: async ({ tagName }: { tagName: string }) => - // h('div', { - // class: `[&>*]:inline-block [&>h1]:m-0 [&>h2]:m-0 [&>h3]:m-0 [&>h4]:m-0 level-${tagName}`, - // tabIndex: -1, - // }), - // content: (heading: Node) => [ - // h( - // `span.anchor-icon`, - // { - // ariaHidden: 'true', - // }, - // LinkIcon, - // ), - // createSROnlyLabel(toString(heading)), - // ], - // }, - // ], + [ + rehypeAutolinkHeadings, + { + properties: { + class: + 'relative inline-flex place-items-center place-content-center outline-none text-black dark:text-white pr-2 -ml-8 opacity-0 group-hover:opacity-100', + }, + behavior: 'prepend', + content: (heading: Node) => [ + h( + `span.anchor-icon`, + { + ariaHidden: 'true', + }, + LinkIcon, + ), + createSROnlyLabel(toString(heading)), + ], + }, + ], ], }, }); diff --git a/apps/guide/package.json b/apps/guide/package.json index cf5ddfacc..d3be5a0f6 100644 --- a/apps/guide/package.json +++ b/apps/guide/package.json @@ -16,7 +16,7 @@ "dev:next": "next dev", "dev:css": "yarn generate:css --watch", "dev:contentlayer": "contentlayer dev", - "generate:css": "unocss 'src/**/*.tsx' '../../packages/ui/src/lib/components/**/*.tsx' --out-file ./src/styles/unocss.css --config ../../unocss.config.ts", + "generate:css": "unocss 'src/**/*.tsx' 'contentlayer.config.ts' '../../packages/ui/src/lib/components/**/*.tsx' --out-file ./src/styles/unocss.css --config ../../unocss.config.ts", "lint": "prettier --check . && cross-env TIMING=1 eslint src --ext .mjs,.js,.cjs,.ts,.tsx --format=pretty", "format": "prettier --write . && cross-env TIMING=1 eslint src --ext .mjs,.js,.cjs,.ts,.tsx --fix --format=pretty", "fmt": "yarn format" @@ -73,6 +73,7 @@ "@next/bundle-analyzer": "^13.3.1", "@testing-library/react": "^14.0.0", "@testing-library/user-event": "^14.4.3", + "@types/html-escaper": "^3.0.0", "@types/node": "18.16.0", "@types/react": "^18.0.38", "@types/react-dom": "^18.0.11", diff --git a/apps/guide/src/components/H1.tsx b/apps/guide/src/components/H1.tsx new file mode 100644 index 000000000..147a88456 --- /dev/null +++ b/apps/guide/src/components/H1.tsx @@ -0,0 +1,9 @@ +import type { HTMLAttributes, PropsWithChildren } from 'react'; + +export default function H1({ children, className, ...props }: PropsWithChildren>) { + return ( +

+ {children} +

+ ); +} diff --git a/apps/guide/src/components/Mdx.tsx b/apps/guide/src/components/Mdx.tsx index db309a479..4ac68c60a 100644 --- a/apps/guide/src/components/Mdx.tsx +++ b/apps/guide/src/components/Mdx.tsx @@ -2,6 +2,7 @@ import { Alert, Section, DiscordMessages, DiscordMessage, DiscordMessageEmbed } from '@discordjs/ui'; import { useMDXComponent } from 'next-contentlayer/hooks'; +import H1 from './H1'; import { DocsLink } from '~/components/DocsLink'; import { ResultingCode } from '~/components/ResultingCode'; @@ -10,7 +11,16 @@ export function Mdx({ code }: { code: string }) { return ( ); } diff --git a/unocss.config.ts b/unocss.config.ts index 4a43e6eed..d619a0f9b 100644 --- a/unocss.config.ts +++ b/unocss.config.ts @@ -48,6 +48,8 @@ export default defineConfig({ display: 'inline-block', }, h1: { + display: 'flex', + 'place-items': 'center', 'scroll-margin-top': '6.5rem', }, '.level-h1': { diff --git a/yarn.lock b/yarn.lock index b2bc7f7b0..cad0a3b73 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2194,6 +2194,7 @@ __metadata: "@react-icons/all-files": ^4.1.0 "@testing-library/react": ^14.0.0 "@testing-library/user-event": ^14.4.3 + "@types/html-escaper": ^3.0.0 "@types/node": 18.16.0 "@types/react": ^18.0.38 "@types/react-dom": ^18.0.11 @@ -6524,6 +6525,13 @@ __metadata: languageName: node linkType: hard +"@types/html-escaper@npm:^3.0.0": + version: 3.0.0 + resolution: "@types/html-escaper@npm:3.0.0" + checksum: 45262fe292d132675abad3db4c9058d18e8a73e3c7e2e5b82a552e73697e5f50fead5142db432cb0685aba5b091a1ea275ae6fb96b8b1a2964ca45f8e50c0fdf + languageName: node + linkType: hard + "@types/http-cache-semantics@npm:*": version: 4.0.1 resolution: "@types/http-cache-semantics@npm:4.0.1"