nextui/apps/docs/components/mdx-components.tsx

239 lines
6.1 KiB
TypeScript

/* eslint-disable react/display-name */
import {clsx} from "@nextui-org/shared-utils";
import * as Components from "@nextui-org/react";
import {Language} from "prism-react-renderer";
import NextImage from "next/image";
import {usePostHog} from "posthog-js/react";
import {ThemeSwitch} from "./theme-switch";
import {Sandpack} from "@/components/sandpack";
import {CarbonAd} from "@/components/ads/carbon-ad";
import * as DocsComponents from "@/components/docs/components";
import * as BlogComponents from "@/components/blog/components";
import {Codeblock} from "@/components/docs/components";
import {VirtualAnchor, virtualAnchorEncode} from "@/components/virtual-anchor";
const Table: React.FC<{children?: React.ReactNode}> = ({children}) => {
return (
<div className="overflow-x-auto overflow-y-hidden">
<table className="border-collapse border-spacing-0 w-full">{children}</table>
</div>
);
};
const Thead: React.FC<{children?: React.ReactNode}> = ({children}) => {
return (
<thead
className={clsx(
"[&>tr]:h-12",
"[&>tr>th]:py-0",
"[&>tr>th]:align-middle",
"[&>tr>th]:bg-default-400/20",
"dark:[&>tr>th]:bg-default-600/10",
"[&>tr>th]:text-default-600 [&>tr>th]:text-xs",
"[&>tr>th]:text-left [&>tr>th]:pl-2",
"[&>tr>th:first-child]:rounded-l-lg",
"[&>tr>th:last-child]:rounded-r-lg",
)}
>
{children}
</thead>
);
};
const Trow: React.FC<{children?: React.ReactNode}> = ({children}) => {
return <tr>{children}</tr>;
};
const Tcol: React.FC<{children?: React.ReactNode}> = ({children}) => {
return (
<td className="text-sm p-2 max-w-[200px] overflow-auto whitespace-normal break-normal">
{children}
</td>
);
};
export interface LinkedHeadingProps {
as: keyof JSX.IntrinsicElements;
id?: string;
linked?: boolean;
children?: React.ReactNode;
className?: string;
}
const linkedLevels: Record<string, number> = {
h1: 0,
h2: 1,
h3: 2,
h4: 3,
};
const LinkedHeading: React.FC<LinkedHeadingProps> = ({
as,
linked = true,
id: idProp,
className,
...props
}) => {
const Component = as;
const level = linkedLevels[as] || 1;
let id = idProp || virtualAnchorEncode(props.children as string);
return (
<Component
className={clsx({"linked-heading": linked}, linked ? {} : className)}
data-id={id}
data-level={level}
data-name={props.children}
id={id}
{...props}
>
{linked ? <VirtualAnchor id={id}>{props.children}</VirtualAnchor> : <>{props.children}</>}
</Component>
);
};
const List: React.FC<{children?: React.ReactNode}> = ({children}) => {
return (
<ul className="list-disc flex flex-col gap-2 ml-4 mt-2 [&>li>strong]:text-pink-500 dark:[&>li>strong]:text-cyan-600">
{children}
</ul>
);
};
const InlineCode = ({children}: {children?: React.ReactNode}) => {
return (
<Components.Code className="font-normal text-default-700 bg-default-200/50 dark:bg-default-100/60 px-2 py-0.5">
{children}
</Components.Code>
);
};
const Code = ({
className,
children,
meta,
}: {
children?: React.ReactNode;
className?: string;
meta?: string;
}) => {
const isMultiLine = (children as string)?.split?.("\n")?.length > 2;
const language = (className?.replace(/language-/, "") ?? "jsx") as Language;
const codeString = String(children).trim();
const posthog = usePostHog();
if (!className) {
return <InlineCode>{children}</InlineCode>;
}
return (
<Components.Snippet
disableTooltip
fullWidth
hideSymbol
classNames={{
base: clsx(
"px-0 bg-code-background text-code-foreground",
{
"items-start": isMultiLine,
},
className,
),
pre: "font-light w-full text-sm",
copyButton: "text-lg text-zinc-500 mr-2",
}}
codeString={codeString}
onCopy={() => {
posthog.capture("MDXComponents - Copy", {
category: "docs",
action: "copyCode",
});
}}
>
<Codeblock codeString={codeString} language={language} metastring={meta} />
</Components.Snippet>
);
};
const Link = ({href, children}: {href?: string; children?: React.ReactNode}) => {
const isExternal = href?.startsWith("http");
const posthog = usePostHog();
const handlePress = () => {
posthog.capture("MDXComponents - Click", {
category: "docs",
action: "click",
data: href || "",
});
};
return (
<Components.Link
href={href}
isExternal={isExternal}
showAnchorIcon={isExternal}
onPress={handlePress}
>
{children}
</Components.Link>
);
};
export const MDXComponents = {
/**
* Next.js components
*/
NextImage,
/**
* NextUI components
*/
...Components,
/**
* Docs components
*/
...DocsComponents,
Sandpack,
ThemeSwitch,
/**
* Blog components
*/
...BlogComponents,
/**
* Markdown components
*/
// ...Icons,
h1: (props: React.HTMLAttributes<HTMLHeadingElement>) => (
<LinkedHeading as="h1" linked={false} {...props} />
),
h2: (props: React.HTMLAttributes<HTMLHeadingElement>) => <LinkedHeading as="h2" {...props} />,
h3: (props: React.HTMLAttributes<HTMLHeadingElement>) => <LinkedHeading as="h3" {...props} />,
h4: (props: React.HTMLAttributes<HTMLHeadingElement>) => <LinkedHeading as="h4" {...props} />,
strong: (props: React.HTMLAttributes<HTMLElement>) => (
<strong className="font-medium" {...props} />
),
table: Table,
thead: Thead,
tr: Trow,
td: Tcol,
CarbonAd,
code: Code,
ul: List,
a: (props: React.HTMLAttributes<HTMLAnchorElement>) => <Link {...props} />,
blockquote: (props: Omit<React.HTMLAttributes<HTMLElement>, "color">) => (
<DocsComponents.Blockquote {...props} />
),
kbd: (props: React.HTMLAttributes<HTMLElement>) => (
<Components.Kbd {...props} className="py-0.5 px-1.5" />
),
Steps: ({...props}) => (
<div
className="[&>h3]:step [&>h3>a]:pt-0.5 [&>h4]:step [&>h4>a]:pt-0.5 mb-12 ml-4 relative border-l border-default-100 pl-[1.625rem] [counter-reset:step]"
{...props}
/>
),
// Block,
} as unknown as Record<string, React.ReactNode>;