docs(api-method): fixed syntax highlighting refreshing on tab change (#4309)

This commit is contained in:
Maxwell
2025-08-30 15:35:44 +10:00
committed by Alex Yang
parent 029268fad0
commit 34afa659de
3 changed files with 131 additions and 28 deletions

View File

@@ -59,7 +59,7 @@ export default async function Page({
>
<DocsTitle>{page.data.title}</DocsTitle>
{!avoidLLMHeader.includes(page.data.title) && (
<div className="flex flex-row gap-2 items-center border-b pb-3">
<div className="flex flex-row gap-2 items-center pb-3 border-b">
<LLMCopyButton />
<ViewOptions
markdownUrl={`${page.url}.mdx`}
@@ -75,7 +75,7 @@ export default async function Page({
return (
<CodeBlockTabs
{...props}
className="bg-fd-secondary border-b p-0 rounded-lg"
className="p-0 rounded-lg border-b bg-fd-secondary"
>
<div {...props}>{props.children}</div>
</CodeBlockTabs>
@@ -85,7 +85,7 @@ export default async function Page({
return (
<CodeBlockTabsList
{...props}
className="bg-fd-secondary my-0 pb-0 rounded-lg"
className="pb-0 my-0 rounded-lg bg-fd-secondary"
/>
);
},
@@ -94,9 +94,9 @@ export default async function Page({
},
pre: (props) => {
return (
<CodeBlock className="bg-fd-muted rounded-xl" {...props}>
<CodeBlock className="rounded-xl bg-fd-muted" {...props}>
<div style={{ minWidth: "100%", display: "table" }}>
<Pre className="bg-fd-muted py-3 px-0 focus-visible:outline-none">
<Pre className="px-0 py-3 bg-fd-muted focus-visible:outline-none">
{props.children}
</Pre>
</div>

View File

@@ -0,0 +1,98 @@
"use client";
import * as React from "react";
import { cn } from "@/lib/utils";
const provider = React.createContext<{
current: string | null;
setCurrent: (value: string | null) => void;
}>({
current: null,
setCurrent: () => {},
});
function ApiMethodTabs({
className,
...props
}: React.ComponentProps<"div"> & { defaultValue: string | null }) {
const [current, setCurrent] = React.useState<string | null>(
props.defaultValue || null,
);
return (
<provider.Provider value={{ current, setCurrent }}>
<div
data-slot="tabs"
className={cn("flex flex-col gap-2", className)}
{...props}
/>
</provider.Provider>
);
}
const useApiMethodTabs = () => {
return React.useContext(provider);
};
function ApiMethodTabsList({
className,
...props
}: React.ComponentProps<"div">) {
return (
<div
data-slot="tabs-list"
className={cn(
"inline-flex justify-center items-center p-1 h-9 rounded-lg bg-muted text-muted-foreground w-fit",
className,
)}
{...props}
/>
);
}
function ApiMethodTabsTrigger({
className,
...props
}: React.ComponentProps<"button"> & { value: string }) {
const { setCurrent, current } = useApiMethodTabs();
return (
<button
data-slot="tabs-trigger"
className={cn(
"data-[state=active]:bg-background data-[state=active]:text-foreground focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:outline-ring inline-flex flex-1 items-center justify-center gap-1.5 rounded-md px-2 py-1 text-sm font-medium whitespace-nowrap transition-[color,box-shadow] focus-visible:ring-[3px] focus-visible:outline-1 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:shadow-sm [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
className,
)}
data-state={props.value === current ? "active" : "inactive"}
onClick={() => {
setCurrent(props.value);
}}
{...props}
/>
);
}
function ApiMethodTabsContent({
className,
...props
}: React.ComponentProps<"div"> & { value: string }) {
const { current } = useApiMethodTabs();
return (
<div
data-slot="tabs-content"
className={cn(
"flex-1 outline-none",
className,
props.value === current && "block",
props.value !== current && "hidden",
)}
{...props}
/>
);
}
export {
ApiMethodTabs,
ApiMethodTabsList,
ApiMethodTabsTrigger,
ApiMethodTabsContent,
};

View File

@@ -9,7 +9,12 @@ import {
TableHeader,
TableRow,
} from "./ui/table";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "./ui/tabs";
import {
ApiMethodTabs,
ApiMethodTabsContent,
ApiMethodTabsList,
ApiMethodTabsTrigger,
} from "./api-method-tabs";
import { JSX, ReactNode } from "react";
import { Link } from "lucide-react";
import { Button } from "./ui/button";
@@ -153,12 +158,12 @@ export const APIMethod = ({
className="absolute invisible -top-[100px]"
/>
</div>
<Tabs
<ApiMethodTabs
defaultValue={isServerOnly ? "server" : "client"}
className="w-full gap-0"
className="gap-0 w-full"
>
<TabsList className="relative flex justify-start w-full p-0 bg-transparent hover:[&>div>a>button]:opacity-100">
<TabsTrigger
<ApiMethodTabsList className="relative flex justify-start w-full p-0 bg-transparent hover:[&>div>a>button]:opacity-100">
<ApiMethodTabsTrigger
value="client"
className="transition-all duration-150 ease-in-out max-w-[100px] data-[state=active]:bg-border hover:bg-border/50 bg-border/50 border hover:border-primary/15 cursor-pointer data-[state=active]:border-primary/10 rounded-none"
>
@@ -179,8 +184,8 @@ export const APIMethod = ({
<path fill="none" d="M0 0h36v36H0z" />
</svg>
<span>Client</span>
</TabsTrigger>
<TabsTrigger
</ApiMethodTabsTrigger>
<ApiMethodTabsTrigger
value="server"
className="transition-all duration-150 ease-in-out max-w-[100px] data-[state=active]:bg-border hover:bg-border/50 bg-border/50 border hover:border-primary/15 cursor-pointer data-[state=active]:border-primary/10 rounded-none"
>
@@ -196,20 +201,20 @@ export const APIMethod = ({
/>
</svg>
<span>Server</span>
</TabsTrigger>
</ApiMethodTabsTrigger>
<div className="absolute right-0">
<a href={`#api-method${pathId}`}>
<Button
variant="ghost"
className="transition-all duration-150 ease-in-out scale-90 opacity-100 md:opacity-0"
className="opacity-100 transition-all duration-150 ease-in-out scale-90 md:opacity-0"
size={"icon"}
>
<Link className="size-4" />
</Button>
</a>
</div>
</TabsList>
<TabsContent value="client">
</ApiMethodTabsList>
<ApiMethodTabsContent value="client">
{isServerOnly ? null : (
<Endpoint
method={method || "GET"}
@@ -228,7 +233,7 @@ export const APIMethod = ({
) : null}
</Note>
) : null}
<div className={cn("w-full relative")}>
<div className={cn("relative w-full")}>
<DynamicCodeBlock
code={`${code_prefix}${
noResult
@@ -240,14 +245,14 @@ export const APIMethod = ({
lang="ts"
/>
{isServerOnly ? (
<div className="absolute inset-0 flex items-center justify-center w-full h-full border rounded-lg backdrop-brightness-50 backdrop-blur-xs border-border">
<div className="flex absolute inset-0 justify-center items-center w-full h-full rounded-lg border backdrop-brightness-50 backdrop-blur-xs border-border">
<span>This is a server-only endpoint</span>
</div>
) : null}
</div>
{!isServerOnly ? <TypeTable props={props} isServer={false} /> : null}
</TabsContent>
<TabsContent value="server">
</ApiMethodTabsContent>
<ApiMethodTabsContent value="server">
{isClientOnly ? null : (
<Endpoint
method={method || "GET"}
@@ -267,17 +272,17 @@ export const APIMethod = ({
) : null}
</Note>
) : null}
<div className={cn("w-full relative")}>
<div className={cn("relative w-full")}>
{serverCodeBlock}
{isClientOnly ? (
<div className="absolute inset-0 flex items-center justify-center w-full h-full border rounded-lg backdrop-brightness-50 backdrop-blur-xs border-border">
<div className="flex absolute inset-0 justify-center items-center w-full h-full rounded-lg border backdrop-brightness-50 backdrop-blur-xs border-border">
<span>This is a client-only endpoint</span>
</div>
) : null}
</div>
{!isClientOnly ? <TypeTable props={props} isServer /> : null}
</TabsContent>
</Tabs>
</ApiMethodTabsContent>
</ApiMethodTabs>
</>
);
};
@@ -328,7 +333,7 @@ function TypeTable({
if (!props.length) return null;
return (
<Table className="mt-2 mb-0 overflow-hidden">
<Table className="overflow-hidden mt-2 mb-0">
<TableHeader>
<TableRow>
<TableHead className="text-primary w-[100px]">Prop</TableHead>
@@ -354,7 +359,7 @@ function TypeTable({
) : null}
</TableCell>
<TableCell className="max-w-[500px] overflow-hidden">
<div className="w-full break-words h-fit text-wrap ">
<div className="w-full break-words h-fit text-wrap">
{tsxifyBackticks(prop.description ?? "")}
</div>
</TableCell>
@@ -709,8 +714,8 @@ function createServerBody({
function Note({ children }: { children: ReactNode }) {
return (
<div className="relative flex flex-col w-full gap-2 p-3 mb-2 break-words border rounded-md text-md text-wrap border-border bg-fd-secondary/50">
<span className="w-full -mb-1 text-xs select-none text-muted-foreground">
<div className="flex relative flex-col gap-2 p-3 mb-2 w-full break-words rounded-md border text-md text-wrap border-border bg-fd-secondary/50">
<span className="-mb-1 w-full text-xs select-none text-muted-foreground">
Notes
</span>
<p className="mt-0 mb-0 text-sm">{children as any}</p>