From 8b0ab5a2d57fea723b6ad0a9a9a40dd30c8b9716 Mon Sep 17 00:00:00 2001 From: ZHANGYU Date: Sun, 2 Jun 2024 21:13:15 +0800 Subject: [PATCH] support CodeGroup component --- package.json | 1 - pnpm-lock.yaml | 8 ----- src/app/(article)/posts/[id]/page.tsx | 2 +- src/markdown/components/alert.tsx | 49 +++++++++++++++++++++++-- src/markdown/components/code-group.tsx | 50 ++++++++++++++------------ src/markdown/markdown.tsx | 13 +++++-- src/markdown/plugins.ts | 34 ------------------ 7 files changed, 86 insertions(+), 71 deletions(-) diff --git a/package.json b/package.json index 0d5d7ab..16a805e 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,6 @@ }, "dependencies": { "@discublog/api": "latest", - "@formkit/auto-animate": "^0.8.2", "@giscus/react": "^3.0.0", "@tabler/icons-react": "^3.5.0", "clsx": "^2.1.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 80c54d4..868026a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,9 +11,6 @@ importers: '@discublog/api': specifier: latest version: 0.0.0-test.11 - '@formkit/auto-animate': - specifier: ^0.8.2 - version: 0.8.2 '@giscus/react': specifier: ^3.0.0 version: 3.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -249,9 +246,6 @@ packages: '@formatjs/intl-localematcher@0.5.4': resolution: {integrity: sha512-zTwEpWOzZ2CiKcB93BLngUX59hQkuZjT2+SAQEscSm52peDW/getsawMcWF1rGRpMCX6D7nSJA3CzJ8gn13N/g==} - '@formkit/auto-animate@0.8.2': - resolution: {integrity: sha512-SwPWfeRa5veb1hOIBMdzI+73te5puUBHmqqaF1Bu7FjvxlYSz/kJcZKSa9Cg60zL0uRNeJL2SbRxV6Jp6Q1nFQ==} - '@giscus/react@3.0.0': resolution: {integrity: sha512-hgCjLpg3Wgh8VbTF5p8ZLcIHI74wvDk1VIFv12+eKhenNVUDjgwNg2B1aq/3puyHOad47u/ZSyqiMtohjy/OOA==} peerDependencies: @@ -3362,8 +3356,6 @@ snapshots: dependencies: tslib: 2.6.2 - '@formkit/auto-animate@0.8.2': {} - '@giscus/react@3.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: giscus: 1.5.0 diff --git a/src/app/(article)/posts/[id]/page.tsx b/src/app/(article)/posts/[id]/page.tsx index 8f72f91..629a1fc 100644 --- a/src/app/(article)/posts/[id]/page.tsx +++ b/src/app/(article)/posts/[id]/page.tsx @@ -103,7 +103,7 @@ export default async function Page({ params }: PageProps) { -
+
({ diff --git a/src/markdown/components/alert.tsx b/src/markdown/components/alert.tsx index b3eb29f..8c83159 100644 --- a/src/markdown/components/alert.tsx +++ b/src/markdown/components/alert.tsx @@ -1,13 +1,56 @@ +import { + IconAlertTriangle, + IconBulb, + IconInfoSquare, + IconMessageReport, + IconUrgent, +} from '@tabler/icons-react' +import clsx from 'clsx' + interface AlertProps { type: 'note' | 'tip' | 'important' | 'warning ' | 'caution' children: React.ReactNode } + +const icons = { + note: IconInfoSquare, + tip: IconBulb, + important: IconMessageReport, + warning: IconAlertTriangle, + caution: IconUrgent, +} + export const Alert = (props: AlertProps) => { const { type, children } = props + + const Icon = icons[type] + + const textClses = { + note: 'text-blue-500', + tip: 'text-green-600', + important: 'text-purple-600', + warning: 'text-yellow-600', + caution: 'text-red-500', + } + + const borderClses = { + note: 'border-blue-500', + tip: 'border-green-600', + important: 'border-purple-600', + warning: 'border-yellow-600', + caution: 'border-red-500', + } + + const textCls = textClses[type] + const borderCls = borderClses[type] + return ( -
-

{type}

+
+

+ + {type} +

{children} -
+ ) } diff --git a/src/markdown/components/code-group.tsx b/src/markdown/components/code-group.tsx index 86de30e..e08adcc 100644 --- a/src/markdown/components/code-group.tsx +++ b/src/markdown/components/code-group.tsx @@ -1,8 +1,8 @@ 'use client' -import React, { useState } from 'react' +import { useState, Children, isValidElement } from 'react' -import { useAutoAnimate } from '@formkit/auto-animate/react' +import clsx from 'clsx' interface CodeGroupProps { 'data-children-meta': string @@ -10,34 +10,40 @@ interface CodeGroupProps { } const CodeGroup = (props: CodeGroupProps) => { const { children } = props - const childrenArray = React.Children.toArray(children) - const metas = JSON.parse(props['data-children-meta']) as string[] - const tabs = metas.map(meta => { - const matches = meta.match(/\[(.+)]/) - if (!matches) { - throw new Error('[markdown:CodeGroup] Meta format error') + const [index, setIndex] = useState(0) + + const childrenArray = Children.toArray(children) + const fileNames = childrenArray.map(child => { + if (isValidElement(child) && isValidElement(child.props.children)) { + return child.props.children.props['data-file'] } - return matches[1] }) - if (tabs.length !== childrenArray.length) { - throw new Error('[markdown:CodeGroup] Meta and children length mismatch') - } - const [index, setIndex] = useState(0) - const [ref] = useAutoAnimate(/* optional config */) + if (fileNames.length !== childrenArray.length) { + return children + } const current = childrenArray[index] return ( -
-
- {tabs.map((tab, index) => ( - setIndex(index)}> - {tab} - +
+
+ {fileNames.map((fileName, i) => ( + ))} -
-
{current}
+ +
{current}
) } diff --git a/src/markdown/markdown.tsx b/src/markdown/markdown.tsx index bcd8e38..6f79b88 100644 --- a/src/markdown/markdown.tsx +++ b/src/markdown/markdown.tsx @@ -1,3 +1,5 @@ +import path from 'node:path' + import rehypeShiki, { type RehypeShikiOptions } from '@shikijs/rehype' import { transformerNotationDiff, @@ -16,7 +18,7 @@ import remarkDirective from 'remark-directive' import remarkGfm from 'remark-gfm' import { MDX, type MDXProps } from 'rsc-mdx' -import { remarkDirectiveContainer, rehypeGithubAlert } from './plugins' +import { rehypeGithubAlert } from './plugins' import { rendererMdx } from './twoslash/renderMdx' interface MarkdownProps { @@ -30,7 +32,7 @@ export async function Markdown(props: MarkdownProps) { { + const metaData = meta.split(' ') + const fileName = metaData.find(item => path.extname(item) !== '') + return { + 'data-file': fileName, + } + }, themes: { light: 'github-light', dark: 'dracula-soft', diff --git a/src/markdown/plugins.ts b/src/markdown/plugins.ts index 528ab43..12b4e7f 100644 --- a/src/markdown/plugins.ts +++ b/src/markdown/plugins.ts @@ -2,41 +2,7 @@ import { isElement } from 'hast-util-is-element' import { visit } from 'unist-util-visit' import type { Text } from 'hast' -import type { Directives } from 'mdast-util-directive' import type { Plugin } from 'unified' -import type { Node } from 'unist' - -const isDirectiveNode = (node: Node): node is Directives => { - const { type } = node - return ( - type === 'textDirective' || - type === 'leafDirective' || - type === 'containerDirective' - ) -} - -export const remarkDirectiveContainer: Plugin = () => tree => - visit(tree, node => { - if (isDirectiveNode(node)) { - if (node.name === 'code-group') { - const childrenMeta = node.children.map(child => child.meta) - node.data = { - hName: 'CodeGroup', - hProperties: { - ...node.attributes, - 'data-children-meta': JSON.stringify(childrenMeta), - }, - } - } else if (node.name === 'details') { - node.data = { - hName: 'Details', - hProperties: { - ...node.attributes, - }, - } - } - } - }) export const rehypeGithubAlert: Plugin = () => tree => visit(tree, node => {