import { Link } from "lucide-react";
import type { JSX, ReactNode } from "react";
import { Children, isValidElement } from "react";
import type { ApiMethodOptions, ApiMethodProperty } from "@/lib/api-method";
import { generateApiMethodExamples, parseApiMethod } from "@/lib/api-method";
import { cn } from "@/lib/utils";
import {
ApiMethodTabs,
ApiMethodTabsContent,
ApiMethodTabsList,
ApiMethodTabsTrigger,
} from "./api-method-tabs";
import { Endpoint } from "./endpoint";
import { Button } from "./ui/button";
import { DynamicCodeBlock } from "./ui/dynamic-code-block";
const apiMethodTabs = [
{
value: "client",
label: "Client",
icon: (
<>
>
),
},
{
value: "server",
label: "Server",
icon: (
<>
>
),
},
] as const;
export function APIMethod({
children,
...options
}: ApiMethodOptions & { children: ReactNode }) {
const {
path,
method,
isServerOnly,
isClientOnly,
isExternalOnly,
note,
clientOnlyNote,
serverOnlyNote,
} = options;
const definition = parseApiMethod(getTextContent(children));
const examples = generateApiMethodExamples(definition, options);
const properties = definition.properties;
const serverTabContent = (
{isClientOnly || isServerOnly ? null : (
)}
{serverOnlyNote || note ? (
{note && tsxifyBackticks(note)}
{serverOnlyNote ? (
<>
{note ?
: null}
{tsxifyBackticks(serverOnlyNote)}
>
) : null}
) : null}
{isClientOnly ? (
This is a client-only endpoint
) : null}
{!isClientOnly ?
: null}
);
if (isExternalOnly) {
return serverTabContent;
}
const pathId = path.replaceAll("/", "-");
return (
<>
{apiMethodTabs.map((tab) => (
{tab.label}
))}
{isServerOnly ? null : (
)}
{clientOnlyNote || note ? (
{note && tsxifyBackticks(note)}
{clientOnlyNote ? (
<>
{note ?
: null}
{tsxifyBackticks(clientOnlyNote)}
>
) : null}
) : null}
{isServerOnly ? (
This is a server-only endpoint
) : null}
{!isServerOnly ? (
) : null}
{serverTabContent}
>
);
}
function getTextContent(node: ReactNode): string {
return Children.toArray(node)
.map((child) =>
isValidElement<{ children?: ReactNode }>(child)
? getTextContent(child.props.children)
: String(child),
)
.join("");
}
function TypeTable({
properties,
isServer,
}: {
properties: ApiMethodProperty[];
isServer: boolean;
}) {
const visibleProperties = properties.filter(
(property) =>
!(property.serverOnly && isServer === false) &&
!(property.clientOnly && isServer === true),
);
if (!visibleProperties.length) return null;
return (
);
}
function PropertyItem({ property }: { property: ApiMethodProperty }) {
return (
{property.name}
{property.type}
{property.nullable ? " | null" : ""}
{!property.optional && (
required
)}
{property.serverOnly && (
server
)}
);
}
function PropertyList({
properties,
nested = false,
}: {
properties: ApiMethodProperty[];
nested?: boolean;
}) {
const groups: {
property: ApiMethodProperty;
children: ApiMethodProperty[];
}[] = [];
let propertyIndex = 0;
while (propertyIndex < properties.length) {
const property = properties[propertyIndex];
if (property.type === "Object") {
const parentSegments = [...property.path, property.name];
const children: ApiMethodProperty[] = [];
propertyIndex++;
while (
propertyIndex < properties.length &&
properties[propertyIndex].path.length >= parentSegments.length &&
parentSegments.every(
(segment, index) => properties[propertyIndex].path[index] === segment,
)
) {
children.push(properties[propertyIndex]);
propertyIndex++;
}
groups.push({ property, children });
} else {
groups.push({ property, children: [] });
propertyIndex++;
}
}
return (
{groups.map((group) => (
0 && "pb-3",
)}
>
{group.property.description && (
{tsxifyBackticks(group.property.description)}
)}
{group.children.length > 0 && (
)}
))}
);
}
function tsxifyBackticks(input: string): JSX.Element {
const parts = input.split(/(`[^`]+`)/g);
return (
<>
{parts.map((part, index) => {
if (part.startsWith("`") && part.endsWith("`")) {
const content = part.slice(1, -1);
return {content};
}
return {part};
})}
>
);
}
function Note({ children }: { children: ReactNode }) {
return (
);
}