Fuma studio

Base URL

Installation

npx shadcn@latest add http://fumastudio/com/r/base-url.tsx
components/fumastudio/base-url.tsx
"use client";

import { colors } from "@/components/fumastudio/colors";
import { useConfig } from "@/components/fumastudio/config-provider";
import { CheckCircleFilled } from "@/components/fumastudio/icons";
import {
	MockRequest,
	useMockRequest,
} from "@/components/fumastudio/mock-request";
import { Button } from "@/components/ui/button";
import { useClipboard } from "@/hooks/use-clipboard";
import { cn } from "@/lib/utils";
import { Copy } from "lucide-react";
import { useState } from "react";

const BaseURL = (props: { url: string; method: string }) => {
	const tokens = props.url.match(/\/|{[^}]+}|[^/]+/g) ?? [];
	const { isCopied, copyValue } = useClipboard();
	const config = useConfig();
	const showTryItButton = config?.enableTryItButton;
	const [showTryItDialog, setShowTryItDialog] = useState(false);

	return (
		<div
			className={`group grid grid-cols-[auto_1fr_auto] gap-x-2 mt-6 border border-input p-2.5 rounded-xl items-center cursor-pointer ${showTryItButton ? "h-[3.3rem]" : "h-12"}`}>
			<div className="flex justify-center">
				<p
					className={`rounded-lg font-bold px-1.5 py-0.5 text-sm leading-5 ${colors(props.method)}`}>
					{props.method}
				</p>
			</div>

			{/* URL parts */}
			<div
				className="overflow-x-auto no-scrollbar"
				onClick={() => copyValue(props.url)}>
				<div className="flex justify-start items-center gap-0.5 font-mono text-sm whitespace-nowrap">
					{tokens.map((item, index) => {
						if (!item) return null;
						const key = `${item}-${index}`;

						if (item === "/") {
							return (
								<span
									key={key}
									className="text-gray-400">
									{item}
								</span>
							);
						}

						if (
							item.startsWith("{") &&
							item.endsWith("}")
						) {
							return (
								<span
									key={key}
									className={`font-medium rounded-md px-px border-2 min-w-max ${colors(props.method, { isSpecial: true })}`}>
									{item}
								</span>
							);
						}
						if (item.startsWith(":")) {
							return (
								<span
									key={key}
									className={`font-medium rounded-md px-px border-2 min-w-max ${colors(props.method, { isSpecial: true })}`}>
									{item}
								</span>
							);
						}

						return (
							<span
								key={key}
								className="font-medium text-sm text-gray-800 dark:text-white min-w-max">
								{item}
							</span>
						);
					})}
				</div>
			</div>

			<div className="flex items-center justify-start text-muted-foreground gap-x-2.5">
				{!isCopied && (
					<Copy className="size-4 cursor-pointer" />
				)}
				{isCopied && (
					<CheckCircleFilled className="size-5 cursor-pointer" />
				)}

				{showTryItButton && (
					<MockRequest
						url={props.url}
						open={showTryItDialog}
						onOpenChange={setShowTryItDialog}>
						<Button
							className="rounded-lg hidden md:flex  cursor-pointer border border-input text-sm font-mono leading-5 tracking-tighter"
							size="sm"
							onClick={() =>
								setShowTryItDialog(
									!showTryItDialog,
								)
							}>
							Try it
						</Button>
					</MockRequest>
				)}
			</div>
		</div>
	);
};

const MockRequestBaseURL = (props: { className?: string; url: string }) => {
	const { baseURL, selectedRequest } = useMockRequest();

	// (...) placeholder for the baseURL
	const url = `...${props.url || ""}`;
	const method = selectedRequest?.method || "GET";

	const tokens = url.match(/\/|{[^}]+}|[^/]+/g) ?? [];
	const { isCopied, copyValue } = useClipboard();

	return (
		<div
			className={cn(
				"group grid grid-cols-[auto_1fr_auto] gap-x-2 border border-input p-1.5 rounded-lg items-center cursor-pointer  h-9",
				props.className,
			)}>
			<div className="flex justify-center">
				<p
					className={`rounded-lg font-bold px-1.5 py-0.5 text-sm leading-5 ${colors(method)}`}>
					{method}
				</p>
			</div>

			{/* URL parts */}
			<div
				className="overflow-x-auto scrollbar-hide"
				onClick={() => copyValue(url)}>
				<div className="flex justify-start items-center gap-0.5 font-mono text-sm whitespace-nowrap">
					{tokens.map((item, index) => {
						if (!item) return null;
						const key = `${item}-${index}`;

						if (item === "/") {
							return (
								<span
									key={key}
									className="text-gray-400">
									{item}
								</span>
							);
						}

						if (
							item.startsWith("{") &&
							item.endsWith("}")
						) {
							return (
								<span
									key={key}
									className={`font-medium rounded-md px-px border-2 min-w-max ${colors(method, { isSpecial: true })}`}>
									{item}
								</span>
							);
						}

						if (item.startsWith("...")) {
							return (
								<span
									key={key}
									className={`font-medium italic transition-all duration-500 group rounded-md px-px border-2 min-w-max ${colors(method, { isSpecial: true })}`}>
									<span className="block group-hover:hidden">
										{item}
									</span>
									<span className="hidden group-hover:flex">
										{baseURL}
									</span>
								</span>
							);
						}

						if (item.startsWith(":")) {
							return (
								<span
									key={key}
									className={`font-medium rounded-md px-px border-2 min-w-max ${colors(method, { isSpecial: true })}`}>
									{item}
								</span>
							);
						}

						return (
							<span
								key={key}
								className="font-medium text-sm text-gray-800 dark:text-white min-w-max">
								{item}
							</span>
						);
					})}
				</div>
			</div>

			<div className=" hidden md:group-hover:flex items-center mr-0.5 text-muted-foreground">
				{!isCopied && (
					<Copy className="size-4 cursor-pointer" />
				)}
				{isCopied && (
					<CheckCircleFilled className="size-5 cursor-pointer" />
				)}
			</div>
		</div>
	);
};

export { BaseURL, MockRequestBaseURL };

Usage

components/fumastudio/api-page.tsx
<BaseURL url="/users" method="GET" />

Props