fumadocs-ui
Version:
The Radix UI version of Fumadocs UI
101 lines (98 loc) • 3.87 kB
JavaScript
'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