mirror of
https://github.com/hanzo-docs/docs.git
synced 2026-10-25 04:20:02 +00:00
Core & UI: Support multiple codeblocks in same tab
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
---
|
||||
'fumadocs-core': patch
|
||||
'fumadocs-ui': patch
|
||||
---
|
||||
|
||||
Support multiple codeblocks in same tab
|
||||
@@ -32,10 +32,10 @@ const Tabs = {
|
||||
withMdx = false,
|
||||
withParent = true,
|
||||
): MdxJsxFlowElement {
|
||||
const names = processTabValue(nodes);
|
||||
const tabs = Array.from(processTabValue(nodes).entries());
|
||||
|
||||
if (!withMdx) {
|
||||
const children: MdxJsxFlowElement[] = nodes.map((node, i) => {
|
||||
const children: MdxJsxFlowElement[] = tabs.map(([name, codes]) => {
|
||||
return {
|
||||
type: 'mdxJsxFlowElement',
|
||||
name: 'Tab',
|
||||
@@ -43,10 +43,10 @@ const Tabs = {
|
||||
{
|
||||
type: 'mdxJsxAttribute',
|
||||
name: 'value',
|
||||
value: names[i],
|
||||
value: name,
|
||||
},
|
||||
],
|
||||
children: [node],
|
||||
children: codes,
|
||||
};
|
||||
});
|
||||
|
||||
@@ -61,7 +61,7 @@ const Tabs = {
|
||||
name: 'items',
|
||||
value: {
|
||||
type: 'mdxJsxAttributeValueExpression',
|
||||
value: names.join(', '),
|
||||
value: tabs.map(([name]) => name).join(', '),
|
||||
data: {
|
||||
estree: {
|
||||
type: 'Program',
|
||||
@@ -72,7 +72,7 @@ const Tabs = {
|
||||
type: 'ExpressionStatement',
|
||||
expression: {
|
||||
type: 'ArrayExpression',
|
||||
elements: names.map((name) => ({
|
||||
elements: tabs.map(([name]) => ({
|
||||
type: 'Literal',
|
||||
value: name,
|
||||
})),
|
||||
@@ -93,7 +93,7 @@ const Tabs = {
|
||||
type: 'mdxJsxFlowElement',
|
||||
name: 'TabsList',
|
||||
attributes: [],
|
||||
children: names.map((name) => ({
|
||||
children: tabs.map(([name]) => ({
|
||||
type: 'mdxJsxFlowElement',
|
||||
name: 'TabsTrigger',
|
||||
attributes: [
|
||||
@@ -103,14 +103,11 @@ const Tabs = {
|
||||
value: name,
|
||||
},
|
||||
],
|
||||
children: [
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- needed
|
||||
mdxToAst(processor, name) as any,
|
||||
],
|
||||
children: [mdxToAst(processor, name) as unknown as BlockContent],
|
||||
})),
|
||||
},
|
||||
...nodes.map(
|
||||
(node, i) =>
|
||||
...tabs.map(
|
||||
([name, codes]) =>
|
||||
({
|
||||
type: 'mdxJsxFlowElement',
|
||||
name: 'TabsContent',
|
||||
@@ -118,10 +115,10 @@ const Tabs = {
|
||||
{
|
||||
type: 'mdxJsxAttribute',
|
||||
name: 'value',
|
||||
value: names[i],
|
||||
value: name,
|
||||
},
|
||||
],
|
||||
children: [node],
|
||||
children: codes,
|
||||
}) as MdxJsxFlowElement,
|
||||
),
|
||||
];
|
||||
@@ -135,7 +132,7 @@ const Tabs = {
|
||||
{
|
||||
type: 'mdxJsxAttribute',
|
||||
name: 'defaultValue',
|
||||
value: names[0],
|
||||
value: tabs[0][0],
|
||||
},
|
||||
],
|
||||
children,
|
||||
@@ -150,24 +147,24 @@ const CodeBlockTabs = {
|
||||
withMdx = false,
|
||||
withParent = true,
|
||||
): MdxJsxFlowElement {
|
||||
const names = processTabValue(nodes);
|
||||
const tabs = Array.from(processTabValue(nodes).entries());
|
||||
|
||||
const node = generateCodeBlockTabs({
|
||||
defaultValue: names[0],
|
||||
triggers: names.map((name) => ({
|
||||
defaultValue: tabs[0][0],
|
||||
triggers: tabs.map(([name]) => ({
|
||||
value: name,
|
||||
children: [
|
||||
withMdx
|
||||
? // eslint-disable-next-line @typescript-eslint/no-explicit-any -- needed
|
||||
(mdxToAst(processor, name) as any)
|
||||
? (mdxToAst(processor, name) as unknown as BlockContent)
|
||||
: {
|
||||
type: 'text',
|
||||
value: name,
|
||||
},
|
||||
],
|
||||
})),
|
||||
tabs: nodes.map((node, i) => ({
|
||||
value: names[i],
|
||||
children: [node],
|
||||
tabs: tabs.map(([name, codes]) => ({
|
||||
value: name,
|
||||
children: codes,
|
||||
})),
|
||||
});
|
||||
|
||||
@@ -256,7 +253,16 @@ export function remarkCodeTab(
|
||||
}
|
||||
|
||||
function processTabValue(nodes: Code[]) {
|
||||
return nodes.map((node, i) => node.data?.tab ?? `Tab ${i + 1}`);
|
||||
const out = new Map<string, Code[]>();
|
||||
|
||||
for (let i = 0; i < nodes.length; i++) {
|
||||
const node = nodes[i];
|
||||
const name = node.data?.tab ?? `Tab ${i + 1}`;
|
||||
const li = out.get(name) ?? [];
|
||||
li.push(node);
|
||||
out.set(name, li);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -77,7 +77,7 @@ export function Pre(props: ComponentProps<'pre'>) {
|
||||
export function CodeBlock({
|
||||
ref,
|
||||
title,
|
||||
allowCopy,
|
||||
allowCopy = true,
|
||||
keepBackground = false,
|
||||
icon,
|
||||
viewportProps = {},
|
||||
@@ -89,7 +89,6 @@ export function CodeBlock({
|
||||
}: CodeBlockProps) {
|
||||
const inTab = useContext(TabsContext) !== null;
|
||||
const areaRef = useRef<HTMLDivElement>(null);
|
||||
allowCopy ??= !inTab;
|
||||
|
||||
return (
|
||||
<figure
|
||||
@@ -97,10 +96,12 @@ export function CodeBlock({
|
||||
dir="ltr"
|
||||
{...props}
|
||||
className={cn(
|
||||
inTab ? 'bg-fd-secondary' : 'my-4 bg-fd-card',
|
||||
inTab
|
||||
? 'bg-fd-secondary -mx-px -mb-px last:rounded-b-xl'
|
||||
: 'my-4 bg-fd-card rounded-xl',
|
||||
keepBackground && 'bg-(--shiki-light-bg) dark:bg-(--shiki-dark-bg)',
|
||||
|
||||
'shiki relative border shadow-sm rounded-xl outline-none not-prose overflow-hidden text-sm',
|
||||
'shiki relative border shadow-sm outline-none not-prose overflow-hidden text-sm',
|
||||
props.className,
|
||||
)}
|
||||
>
|
||||
@@ -221,23 +222,15 @@ export function CodeBlockTabs({ ref, ...props }: ComponentProps<typeof Tabs>) {
|
||||
}
|
||||
|
||||
export function CodeBlockTabsList(props: ComponentProps<typeof TabsList>) {
|
||||
const { containerRef, nested } = useContext(TabsContext)!;
|
||||
|
||||
return (
|
||||
<TabsList
|
||||
{...props}
|
||||
className={cn(
|
||||
'flex flex-row *:first:ms-2 overflow-x-auto text-fd-muted-foreground',
|
||||
'flex flex-row px-2 overflow-x-auto text-fd-muted-foreground',
|
||||
props.className,
|
||||
)}
|
||||
>
|
||||
{props.children}
|
||||
{!nested && (
|
||||
<CopyButton
|
||||
className="sticky ms-auto right-2 bg-fd-card backdrop-blur-sm"
|
||||
containerRef={containerRef}
|
||||
/>
|
||||
)}
|
||||
</TabsList>
|
||||
);
|
||||
}
|
||||
@@ -260,10 +253,7 @@ export function CodeBlockTabsTrigger({
|
||||
);
|
||||
}
|
||||
|
||||
// TODO: currently Vite RSC plugin has problem with adding `asChild` here, maybe revisit this in future
|
||||
export function CodeBlockTab({
|
||||
className,
|
||||
...props
|
||||
}: ComponentProps<typeof TabsContent>) {
|
||||
return <TabsContent className={cn('-mx-px -mb-px', className)} {...props} />;
|
||||
// TODO: currently Vite RSC plugin has problem with `asChild` due to children is automatically wrapped in <Fragment />, maybe revisit this in future
|
||||
export function CodeBlockTab(props: ComponentProps<typeof TabsContent>) {
|
||||
return <TabsContent {...props} />;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user