next.js 使用数据属性的MDX自定义组件

v440hwme  于 2023-06-29  发布在  其他
关注(0)|答案(1)|浏览(127)

是否可以使用HTML元素的data-attribute定义自定义MDX组件?
我正在写一个Next.js博客,使用contentlayer作为MDX,Rehype Pretty Code作为语法高亮代码块。下面是Rehype Pretty代码的HTML输出结构。我想为下面的HTML创建一个自定义MDX组件,但根元素是<div>。那么,是否可以使用div元素的data属性(在本例中为div[data-rehype-pretty-code-fragment])来创建自定义MDX组件?

<div data-rehype-pretty-code-fragment>
    <div data-rehype-pretty-code-title="" data-language="js">sample.js</div>
    <pre data-language="js">
        <code data-line-numbers data-line-numbers-max-digits="2">
            </span data-line="">
            </span style="color:#C678DD">const</span>
            <!-- more span elements ... -->
        </code>
    </pre>
    <div data-rehype-pretty-code-caption>this is a sample caption</div>
</div>

为简洁起见,上述代码块被缩短
自定义MDX组件:
能做到吗?

export const components: MDXComponents = {
    // Add a custom component.
    MyComponent: () => <div>Hello World!</div>,
    div[data-rehype-pretty-code-fragment]: (props) => <CodeBlock {...props}  />, // is this possible?
};

我的需求

我想做的是使用自定义JSX组件向代码块输出添加额外的DOM元素和类,如下面的类codeBlockRootcustomContentmoreCustomContent,并向整个代码块添加样式。现在我必须使用div[data-rehype-pretty-code-fragment]来向代码块添加样式:

<div class="codeBlockRoot" data-rehype-pretty-code-fragment>
    <div class="customContent">this is custom HTML</div>
    <div data-rehype-pretty-code-title="" data-language="js">
        sample.js
        <div class="moreCustomContent">more custom content</div>
    </div>
    <pre data-language="js">
        <code data-line-numbers data-line-numbers-max-digits="2">
            </span data-line="">
            </span style="color:#C678DD">const</span>
            <!-- more span elements ... -->
        </code>
    </pre>
    <div data-rehype-pretty-code-caption>this is a sample caption</div>
</div>
ac1kyiln

ac1kyiln1#

我相信以下解决方案符合您的标准。

// mdx-components.tsx

import * as React from "react";
import type { MDXComponents } from "mdx/types";
import CodeBlock from "@/components/CodeBlock";

declare module "react" {
  interface HTMLAttributes<T> {
    [key: `data-${string}`]: unknown;
  }
}

export function useMDXComponents(components: MDXComponents): MDXComponents {
  return {
    div: ({ children, ...rest }) =>
      React.createElement(
        rest["data-rehype-pretty-code-fragment"] !== undefined
          ? CodeBlock
          : "div",
        rest,
        children
      ),
    ...components,
  };
}
// CodeBlock.tsx

import * as React from "react";

export default function CodeBlock({
  className,
  children,
  ...rest
}: React.JSX.IntrinsicElements["div"]) {
  return (
    <div
      className={[className, "codeBlockRoot"].filter(Boolean).join(" ")}
      {...rest}
    >
      {React.Children.map(children, (child) =>
        React.isValidElement(child) &&
        child.props["data-rehype-pretty-code-title"] !== undefined ? (
          <>
            <div className="customContent">this is custom HTML</div>
            {React.cloneElement(child, child.props, [
              ...React.Children.toArray(child.props.children),
              <div key="moreCustomContent" className="moreCustomContent">
                more custom content
              </div>,
            ])}
          </>
        ) : (
          child
        )
      )}
    </div>
  );
}

相关问题