From adc0461fc38b67cf1104c3a8f80c614a28f0aee6 Mon Sep 17 00:00:00 2001 From: gautamgambhir97 <140384949+gautamgambhir97@users.noreply.github.com> Date: Tue, 17 Sep 2024 13:43:16 +0530 Subject: [PATCH] feat(misc): update the api code snippet (#950) --- components/api-endpoint.tsx | 115 +++++++++++++++++++++++++-------- components/mdx.tsx | 93 +++++++++++++++++++++------ src/icons/shared-icons.tsx | 123 ++++++++++++++++++++++++++++++++++++ styles/globals.css | 96 ++++++++++++++++++++++++++++ 4 files changed, 379 insertions(+), 48 deletions(-) diff --git a/components/api-endpoint.tsx b/components/api-endpoint.tsx index 9274285db..1d36ddd3b 100644 --- a/components/api-endpoint.tsx +++ b/components/api-endpoint.tsx @@ -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, @@ -11,7 +12,7 @@ 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; @@ -19,6 +20,70 @@ interface PropertyType { 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(null); + + const handleCopy = () => { + if (codeRef.current?.textContent) { + navigator.clipboard.writeText(codeRef.current.textContent).then(() => { + setIsCopied(true); + setTimeout(() => setIsCopied(false), 5000); + }); + } + }; + + return ( +
+      
+ + {`${filename} command`} + {hasCopyCode && ( +
+ {isCopied && ( + <> + + + + Copied + + )} + +
+ )} +
+
+ {children} +
+
+ ); +}; + // Helper function to replace path parameters in the URL const replacePathParameters = ( path: string, @@ -74,16 +139,11 @@ requests.${method.toLowerCase()}("${actualUrl}", headers={ `; return ( -
+    
       
         {code}
       
-    
+ ); }; @@ -130,11 +190,10 @@ await fetch("${actualUrl}", { })`; return ( -
       
         {code}
       
-    
+ ); }; +const escapeQuotes = (jsonObject) => { + const jsonString = JSON.stringify(jsonObject); + const escapedString = jsonString.replaceAll(/(? = ({ method, url, samplePayload, isBearerTokenRequired }) => { - let code = `\ -curl \\ --X ${method} \\ + let code = ` +curl -X ${method} ${ isBearerTokenRequired - ? `-H Authorization: bearer -H 'Content-Type: application/json' \\\n` + ? ` -H "Authorization: bearer " -H "Content-Type: application/json"\n ` : "" }${url}`; if (samplePayload) { - code += ` \\\n -d '${JSON.stringify(samplePayload)}'`; + code += ` -d ${JSON.stringify(escapeQuotes(samplePayload))}`; } return ( -
+    
       
         {code.split("\n").map((line) => {
           return (
@@ -183,7 +242,7 @@ ${
           );
         })}
       
-    
+ ); }; @@ -193,9 +252,9 @@ const JsonCodeTab: React.FC<{ const formattedJson = JSON.stringify(samplePayload, undefined, 2); return ( -
+    
       {formattedJson}
-    
+ ); }; diff --git a/components/mdx.tsx b/components/mdx.tsx index b20079952..0686fdb25 100644 --- a/components/mdx.tsx +++ b/components/mdx.tsx @@ -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 ( @@ -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[] | React.ReactElement; +} + +export function DropDownTabs({ children }: DropDownTabsProps) { const [selectedTab, setSelectedTab] = useState(0); + const [isOpen, setIsOpen] = useState(false); + const dropdownRef = useRef(null); + + const tabs = React.Children.toArray( + children, + ) as React.ReactElement[]; - 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 ( -
- - {React.Children.map(children, (child, index) => - index === selectedTab ? child.props.children : undefined, - )} -
+ {tabs[selectedTab]?.props.heading} + + +
+ {isOpen && ( + + {tabs.map((child, index) => ( +
handleTabChange(index)} + > + {child?.props.heading} + {index === selectedTab && } +
+ ))} +
+ )} +
{tabs[selectedTab]?.props.children}
+
+ ); } diff --git a/src/icons/shared-icons.tsx b/src/icons/shared-icons.tsx index 37dc0bbb6..3231ed888 100644 --- a/src/icons/shared-icons.tsx +++ b/src/icons/shared-icons.tsx @@ -201,3 +201,126 @@ export const vectorPointer = () => { ); }; + +export const CodeIcon = () => { + return ( + + + + + + + + + + + + + + + ); +}; + +export const CopyIcon = () => { + return ( + + + + ); +}; + +export const Tick = () => { + return ( + + + + ); +}; + +export const DropDownArrow = () => { + return ( + + + + ); +}; + +export const Tickicon = () => { + return ( + + + + ); +}; diff --git a/styles/globals.css b/styles/globals.css index b64a8e090..d1c7e32cc 100644 --- a/styles/globals.css +++ b/styles/globals.css @@ -4759,3 +4759,99 @@ div:hover > .\[div\:hover\>\&\]\:nx-opacity-100 { max-width: 100%; } } + +.custom-pre { + display: flex; + padding: 20px 24px 24px 24px; + flex-direction: column; + align-items: flex-start; + gap: 20px; + align-self: stretch; + border-radius: 12px; + border: 0.5px solid rgba(255, 255, 255, 0.7); + background: rgba(52, 52, 52, 0.08); + box-shadow: 10px 20px 100px 0px rgba(243, 245, 248, 0.3) inset; + overflow-x: hidden; +} + +.nx-code-name { + color: var(--Grey-Grey-500, #87929c); + font-family: Lexend; + font-size: 14px; + font-style: normal; + font-weight: 400; + line-height: 180%; +} + +.nx-copy-text { + color: #0b1742; + font-family: Lexend; + font-size: 14px; + font-style: normal; + font-weight: 400; + line-height: normal; + letter-spacing: 0.14px; +} + +.nx-scrollable-code { + white-space: pre; +} + +.nx-dropdown-container:hover { + background: #efebff; +} + +.nx-dropdown-container { + display: flex; + max-width: 154px; + padding: 12px 24px; + justify-content: space-between; + align-items: center; + border-radius: 8px; + border: 1px solid rgba(0, 0, 0, 0.2); + color: #000d3d; + font-family: Lexend; + font-size: 16px; + font-style: normal; + font-weight: 400; + line-height: normal; +} + +.dropDown { + display: flex; + width: 192px; + padding: 4px; + flex-direction: column; + align-items: flex-start; + border-radius: 8px; + border: 1px solid #fff; + background: #fff; + box-shadow: 0px 30px 60px 0px rgba(0, 0, 0, 0.1); +} + +.dropdown-tab:hover { + background: #efebff; +} + +.dropdown-tab { + display: flex; + padding: 12px; + align-items: center; + gap: 18px; + align-self: stretch; + border-radius: 4px; +} + +.tab-text { + color: #000d3d; + text-align: center; + font-family: Lexend; + font-size: 16px; + font-style: normal; + font-weight: 400; + line-height: 150%; +} + +.indgo-100 { + background: #efebff; +}