Skip to content

Commit

Permalink
feat(misc): update the api code snippet (#950)
Browse files Browse the repository at this point in the history
  • Loading branch information
gautamgambhir97 authored Sep 17, 2024
1 parent 7eac1a5 commit adc0461
Show file tree
Hide file tree
Showing 4 changed files with 379 additions and 48 deletions.
115 changes: 87 additions & 28 deletions components/api-endpoint.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Code, Pre } from "nextra/components";
import React, { useState } from "react";
import { useRef } from "react";
import React, { ReactNode, useState } from "react";
import {
ApiIntro,
Col,
Expand All @@ -11,14 +12,78 @@ import {
} from "./mdx";
import Tooltip from "./tooltip";
import Link from "next/link";

import { CodeIcon, CopyIcon } from "src/icons/shared-icons";
interface PropertyType {
name: string;
type: string;
description: string;
required?: boolean;
}

const CustomPre = ({
children,
filename,
dataLanguage,
hasCopyCode = true,
}: {
children: ReactNode;
filename?: string;
dataLanguage?: string;
hasCopyCode?: boolean;
}) => {
const [isCopied, setIsCopied] = useState(false);
const codeRef = useRef<HTMLDivElement>(null);

const handleCopy = () => {
if (codeRef.current?.textContent) {
navigator.clipboard.writeText(codeRef.current.textContent).then(() => {
setIsCopied(true);
setTimeout(() => setIsCopied(false), 5000);
});
}
};

return (
<Pre data-language={dataLanguage} className="custom-pre">
<div
style={{ overflowX: "hidden" }}
className="nx-flex nx-w-full nx-items-center nx-justify-between"
>
<CodeIcon />
<span className="nx-code-name">{`${filename} command`}</span>
{hasCopyCode && (
<div
onClick={handleCopy}
className="nx-cursor-pointer nx-flex nx-gap-2 nx-items-center"
>
{isCopied && (
<>
<svg
width="12"
height="8"
viewBox="0 0 12 8"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M11.3359 0.414062C11.5469 0.648438 11.5469 1 11.3359 1.21094L5.14844 7.39844C4.91406 7.63281 4.5625 7.63281 4.35156 7.39844L1.16406 4.21094C0.929688 4 0.929688 3.64844 1.16406 3.4375C1.375 3.20312 1.72656 3.20312 1.9375 3.4375L4.72656 6.22656L10.5391 0.414062C10.75 0.203125 11.1016 0.203125 11.3125 0.414062H11.3359Z"
fill="#0B1742"
/>
</svg>
<span className="nx-copy-text">Copied</span>
</>
)}
<CopyIcon />
</div>
)}
</div>
<div style={{ overflowX: "scroll", width: "100%" }} ref={codeRef}>
{children}
</div>
</Pre>
);
};

// Helper function to replace path parameters in the URL
const replacePathParameters = (
path: string,
Expand Down Expand Up @@ -74,16 +139,11 @@ requests.${method.toLowerCase()}("${actualUrl}", headers={
`;

return (
<Pre
filename="python"
data-language="python"
hasCopyCode={true}
className="nx-pre-code"
>
<CustomPre filename="python" dataLanguage="python" hasCopyCode={true}>
<Code data-lanuage="python" data-theme="default">
{code}
</Code>
</Pre>
</CustomPre>
);
};

Expand Down Expand Up @@ -130,11 +190,10 @@ await fetch("${actualUrl}", {
})`;

return (
<Pre
filename="javascript"
data-lanuage="javascript"
<CustomPre
hasCopyCode={true}
className="nx-pre-code"
dataLanguage="javascript"
filename="javascript"
>
<Code
data-lanuage="javascript"
Expand All @@ -143,36 +202,36 @@ await fetch("${actualUrl}", {
>
{code}
</Code>
</Pre>
</CustomPre>
);
};

const escapeQuotes = (jsonObject) => {
const jsonString = JSON.stringify(jsonObject);
const escapedString = jsonString.replaceAll(/(?<!\\)"/g, '"');
return escapedString;
};

const CurlCodeTab: React.FC<{
method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
url: string;
samplePayload?: unknown;
isBearerTokenRequired?: boolean;
}> = ({ method, url, samplePayload, isBearerTokenRequired }) => {
let code = `\
curl \\
-X ${method} \\
let code = `
curl -X ${method}
${
isBearerTokenRequired
? `-H Authorization: bearer <your token here> -H 'Content-Type: application/json' \\\n`
? ` -H "Authorization: bearer <your token here>" -H "Content-Type: application/json"\n `
: ""
}${url}`;

if (samplePayload) {
code += ` \\\n -d '${JSON.stringify(samplePayload)}'`;
code += ` -d ${JSON.stringify(escapeQuotes(samplePayload))}`;
}

return (
<Pre
filename="bash"
data-lanuage="curl"
hasCopyCode={true}
className="nx-pre-code"
>
<CustomPre hasCopyCode={true} dataLanguage="Curl" filename="Curl">
<Code data-lanuage="bash" data-theme="default">
{code.split("\n").map((line) => {
return (
Expand All @@ -183,7 +242,7 @@ ${
);
})}
</Code>
</Pre>
</CustomPre>
);
};

Expand All @@ -193,9 +252,9 @@ const JsonCodeTab: React.FC<{
const formattedJson = JSON.stringify(samplePayload, undefined, 2);

return (
<Pre className="nx-pre-code" filename="json" hasCopyCode={true}>
<CustomPre filename="json" hasCopyCode={true}>
{formattedJson}
</Pre>
</CustomPre>
);
};

Expand Down
93 changes: 73 additions & 20 deletions components/mdx.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import React, { ReactNode, useState } from "react";
import React, { ReactNode, useState, useEffect, useRef } from "react";
import { Tab as HeadlessTab } from "@headlessui/react";
import { DropDownArrow, Tickicon } from "src/icons/shared-icons";
import { motion } from "framer-motion";

function InfoIcon(properties) {
return (
Expand Down Expand Up @@ -155,30 +157,81 @@ export function Tabs({
);
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function DropDownTabs({ children }: { children: any }) {
interface TabProps {
heading: string;
children: ReactNode;
}

interface DropDownTabsProps {
children: React.ReactElement<TabProps>[] | React.ReactElement<TabProps>;
}

export function DropDownTabs({ children }: DropDownTabsProps) {
const [selectedTab, setSelectedTab] = useState(0);
const [isOpen, setIsOpen] = useState(false);
const dropdownRef = useRef<HTMLDivElement>(null);

const tabs = React.Children.toArray(
children,
) as React.ReactElement<TabProps>[];

const handleTabChange = (event) => {
setSelectedTab(Number.parseInt(event.target.value));
const handleTabChange = (index: number) => {
setSelectedTab(index);
setIsOpen(false);
};

useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
if (
dropdownRef.current &&
!dropdownRef.current.contains(event.target as Node)
) {
setIsOpen(false);
}
};

document.addEventListener("mousedown", handleClickOutside);
return () => {
document.removeEventListener("mousedown", handleClickOutside);
};
}, [dropdownRef]);

return (
<div>
<select
className="pr-6 outline-none nx-rounded-lg nx-p-4 nx-text-sm nx-font-medium nx-bg-white nx-border nx-border-black/10"
value={selectedTab}
onChange={handleTabChange}
<>
<div
className="nx-relative nx-mb-5 nx-items-center nx-gap-2 nx-cursor-pointer nx-justify-center nx-dropdown-container"
onClick={() => setIsOpen(!isOpen)}
style={{ userSelect: "none" }}
>
{React.Children.map(children, (child, index) => (
<option key={index} value={index}>
{child.props.heading}
</option>
))}
</select>
{React.Children.map(children, (child, index) =>
index === selectedTab ? child.props.children : undefined,
)}
</div>
{tabs[selectedTab]?.props.heading}
<DropDownArrow />
</div>
<div ref={dropdownRef}>
{isOpen && (
<motion.div
initial={{ height: 0, opacity: 0 }}
animate={{ height: isOpen ? "auto" : 0, opacity: isOpen ? 1 : 0 }}
transition={{ duration: 0.1 }}
className="nx-z-50 nx-absolute dropDown"
style={{ overflow: "hidden" }}
>
{tabs.map((child, index) => (
<div
key={index}
className={`dropdown-tab nx-justify-between tab-text nx-cursor-pointer ${
index === selectedTab && `indgo-100`
}`}
onClick={() => handleTabChange(index)}
>
{child?.props.heading}
{index === selectedTab && <Tickicon />}
</div>
))}
</motion.div>
)}
<div className="mt-4">{tabs[selectedTab]?.props.children}</div>
</div>
</>
);
}

Expand Down
Loading

0 comments on commit adc0461

Please sign in to comment.