SPB Git forge

spb/fetcha

Public
11commits 1branches 0releases
1.5 MBsize
maindefault branch
16 days agolast push
TypeScript 97.5% SQL 1.4% Python 0.8%
2.2 KB · 65 lines tsx
Raw Blame History
1"use client";2import * as React from "react";3import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";4import { CodeBlock, type CodeLang } from "@/components/ui/code-block";5import { cn } from "@/lib/utils";67export interface CodeTab {8  /** Tab label, e.g. "cURL". Also used as the tab value. */9  label: string;10  lang: CodeLang;11  code: string;12  title?: string;13}1415const STORAGE_KEY = "fetcha-docs-lang";16const EVENT = "fetcha-docs-lang-change";1718/**19 * Language switcher for code samples. The selected language is remembered in localStorage20 * and synchronised across every CodeTabs instance on the page.21 */22export function CodeTabs({ tabs, className, maxHeight, lineNumbers }: { tabs: CodeTab[]; className?: string; maxHeight?: number | string; lineNumbers?: boolean }) {23  const labels = React.useMemo(() => tabs.map((t) => t.label), [tabs]);24  const [value, setValue] = React.useState(labels[0] ?? "");2526  React.useEffect(() => {27    const apply = (stored: string | null) => {28      if (stored && labels.includes(stored)) setValue(stored);29    };30    try {31      apply(window.localStorage.getItem(STORAGE_KEY));32    } catch {}33    const onChange = (e: Event) => apply((e as CustomEvent<string>).detail);34    window.addEventListener(EVENT, onChange);35    return () => window.removeEventListener(EVENT, onChange);36  }, [labels]);3738  const onValueChange = (v: string) => {39    setValue(v);40    try {41      window.localStorage.setItem(STORAGE_KEY, v);42    } catch {}43    window.dispatchEvent(new CustomEvent(EVENT, { detail: v }));44  };4546  if (!tabs.length) return null;4748  return (49    <Tabs value={value} onValueChange={onValueChange} className={cn("my-5", className)}>50      <TabsList variant="underline" aria-label="Code language" className="gap-3">51        {tabs.map((t) => (52          <TabsTrigger key={t.label} value={t.label} className="text-[12.5px]">53            {t.label}54          </TabsTrigger>55        ))}56      </TabsList>57      {tabs.map((t) => (58        <TabsContent key={t.label} value={t.label} className="mt-3">59          <CodeBlock code={t.code} lang={t.lang} title={t.title} maxHeight={maxHeight} lineNumbers={lineNumbers} />60        </TabsContent>61      ))}62    </Tabs>63  );64}65