UNPKG

fumadocs-ui

Version:

The Radix UI version of Fumadocs UI

101 lines (98 loc) 3.87 kB
'use client'; import { Tabs as Tabs$1, TabsContent as TabsContent$1, TabsList as TabsList$1, TabsTrigger as TabsTrigger$1 } from "./ui/tabs.js"; import { cn } from "@fumadocs/ui/cn"; import { jsx, jsxs } from "react/jsx-runtime"; import * as React from "react"; import { createContext, useContext, useEffect, useId, useMemo, useState } from "react"; //#region src/components/tabs.tsx const TabsContext = createContext(null); function useTabContext() { const ctx = useContext(TabsContext); if (!ctx) throw new Error("You must wrap your component in <Tabs>"); return ctx; } const TabsList = React.forwardRef((props, ref) => /* @__PURE__ */ jsx(TabsList$1, { ref, ...props, className: cn("flex gap-3.5 text-fd-secondary-foreground overflow-x-auto px-4 not-prose", props.className) })); TabsList.displayName = "TabsList"; const TabsTrigger = React.forwardRef((props, ref) => /* @__PURE__ */ jsx(TabsTrigger$1, { ref, ...props, className: cn("inline-flex items-center gap-2 whitespace-nowrap text-fd-muted-foreground border-b border-transparent py-2 text-sm font-medium transition-colors [&_svg]:size-4 hover:text-fd-accent-foreground disabled:pointer-events-none disabled:opacity-50 data-[state=active]:border-fd-primary data-[state=active]:text-fd-primary", props.className) })); TabsTrigger.displayName = "TabsTrigger"; function Tabs({ ref, className, items, label, defaultIndex = 0, defaultValue = items ? escapeValue(items[defaultIndex]) : void 0, ...props }) { const [value, setValue] = useState(defaultValue); const collection = useMemo(() => [], []); return /* @__PURE__ */ jsxs(Tabs$1, { ref, className: cn("flex flex-col overflow-hidden rounded-xl border bg-fd-secondary my-4", className), value, onValueChange: (v) => { if (items && !items.some((item) => escapeValue(item) === v)) return; setValue(v); }, ...props, children: [items && /* @__PURE__ */ jsxs(TabsList, { children: [label && /* @__PURE__ */ jsx("span", { className: "text-sm font-medium my-auto me-auto", children: label }), items.map((item) => /* @__PURE__ */ jsx(TabsTrigger, { value: escapeValue(item), children: item }, item))] }), /* @__PURE__ */ jsx(TabsContext.Provider, { value: useMemo(() => ({ items, collection }), [collection, items]), children: props.children })] }); } function Tab({ value, ...props }) { const { items } = useTabContext(); const resolved = value ?? items?.at(useCollectionIndex()); if (!resolved) throw new Error("Failed to resolve tab `value`, please pass a `value` prop to the Tab component."); return /* @__PURE__ */ jsx(TabsContent, { value: escapeValue(resolved), ...props, children: props.children }); } function TabsContent({ value, className, ...props }) { return /* @__PURE__ */ jsx(TabsContent$1, { value, forceMount: true, className: cn("p-4 text-[0.9375rem] bg-fd-background rounded-xl outline-none prose-no-margin data-[state=inactive]:hidden [&>figure:only-child]:-m-4 [&>figure:only-child]:border-none", className), ...props, children: props.children }); } /** * Inspired by Headless UI. * * Return the index of children, this is made possible by registering the order of render from children using React context. * This is supposed by work with pre-rendering & pure client-side rendering. */ function useCollectionIndex() { const key = useId(); const { collection } = useTabContext(); useEffect(() => { return () => { const idx = collection.indexOf(key); if (idx !== -1) collection.splice(idx, 1); }; }, [key, collection]); if (!collection.includes(key)) collection.push(key); return collection.indexOf(key); } /** * only escape whitespaces in values in simple mode */ function escapeValue(v) { return v.toLowerCase().replace(/\s/, "-"); } //#endregion export { Tab, Tabs, TabsContent, TabsList, TabsTrigger }; //# sourceMappingURL=tabs.js.map