fumadocs-ui
Version:
The Radix UI version of Fumadocs UI
126 lines (123 loc) • 4.94 kB
JavaScript
'use client';
import { buttonVariants } from "./ui/button.js";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "./ui/tabs.js";
import { cn } from "@fumadocs/ui/cn";
import { jsx, jsxs } from "react/jsx-runtime";
import { Check, Clipboard } from "lucide-react";
import { createContext, use, useMemo, useRef } from "react";
import { useCopyButton } from "@fumadocs/ui/hooks/use-copy-button";
import { mergeRefs } from "@fumadocs/ui/merge-refs";
//#region src/components/codeblock.tsx
const TabsContext = createContext(null);
function Pre(props) {
return /* @__PURE__ */ jsx("pre", {
...props,
className: cn("min-w-full w-max *:flex *:flex-col", props.className),
children: props.children
});
}
function CodeBlock({ ref, title, allowCopy = true, keepBackground = false, icon, viewportProps = {}, children, Actions = (props$1) => /* @__PURE__ */ jsx("div", {
...props$1,
className: cn("empty:hidden", props$1.className)
}), ...props }) {
const inTab = use(TabsContext) !== null;
const areaRef = useRef(null);
return /* @__PURE__ */ jsxs("figure", {
ref,
dir: "ltr",
...props,
tabIndex: -1,
className: cn(inTab ? "bg-fd-secondary -mx-px -mb-px last:rounded-b-xl" : "my-4 bg-fd-card rounded-xl", keepBackground && "bg-(--shiki-light-bg) dark:bg-(--shiki-dark-bg)", "shiki relative border shadow-sm not-prose overflow-hidden text-sm", props.className),
children: [title ? /* @__PURE__ */ jsxs("div", {
className: "flex text-fd-muted-foreground items-center gap-2 h-9.5 border-b px-4",
children: [
typeof icon === "string" ? /* @__PURE__ */ jsx("div", {
className: "[&_svg]:size-3.5",
dangerouslySetInnerHTML: { __html: icon }
}) : icon,
/* @__PURE__ */ jsx("figcaption", {
className: "flex-1 truncate",
children: title
}),
Actions({
className: "-me-2",
children: allowCopy && /* @__PURE__ */ jsx(CopyButton, { containerRef: areaRef })
})
]
}) : Actions({
className: "absolute top-3 right-2 z-2 backdrop-blur-lg rounded-lg text-fd-muted-foreground",
children: allowCopy && /* @__PURE__ */ jsx(CopyButton, { containerRef: areaRef })
}), /* @__PURE__ */ jsx("div", {
ref: areaRef,
...viewportProps,
role: "region",
tabIndex: 0,
className: cn("text-[0.8125rem] py-3.5 overflow-auto max-h-[600px] fd-scroll-container focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-fd-ring", viewportProps.className),
style: {
"--padding-right": !title ? "calc(var(--spacing) * 8)" : void 0,
counterSet: props["data-line-numbers"] ? `line ${Number(props["data-line-numbers-start"] ?? 1) - 1}` : void 0,
...viewportProps.style
},
children
})]
});
}
function CopyButton({ className, containerRef, ...props }) {
const [checked, onClick] = useCopyButton(() => {
const pre = containerRef.current?.getElementsByTagName("pre").item(0);
if (!pre) return;
const clone = pre.cloneNode(true);
clone.querySelectorAll(".nd-copy-ignore").forEach((node) => {
node.replaceWith("\n");
});
navigator.clipboard.writeText(clone.textContent ?? "");
});
return /* @__PURE__ */ jsx("button", {
type: "button",
"data-checked": checked || void 0,
className: cn(buttonVariants({
className: "hover:text-fd-accent-foreground data-checked:text-fd-accent-foreground",
size: "icon-xs"
}), className),
"aria-label": checked ? "Copied Text" : "Copy Text",
onClick,
...props,
children: checked ? /* @__PURE__ */ jsx(Check, {}) : /* @__PURE__ */ jsx(Clipboard, {})
});
}
function CodeBlockTabs({ ref, ...props }) {
const containerRef = useRef(null);
const nested = use(TabsContext) !== null;
return /* @__PURE__ */ jsx(Tabs, {
ref: mergeRefs(containerRef, ref),
...props,
className: cn("bg-fd-card rounded-xl border", !nested && "my-4", props.className),
children: /* @__PURE__ */ jsx(TabsContext, {
value: useMemo(() => ({
containerRef,
nested
}), [nested]),
children: props.children
})
});
}
function CodeBlockTabsList(props) {
return /* @__PURE__ */ jsx(TabsList, {
...props,
className: cn("flex flex-row px-2 overflow-x-auto text-fd-muted-foreground", props.className),
children: props.children
});
}
function CodeBlockTabsTrigger({ children, ...props }) {
return /* @__PURE__ */ jsxs(TabsTrigger, {
...props,
className: cn("relative group inline-flex text-sm font-medium text-nowrap items-center transition-colors gap-2 px-2 py-1.5 hover:text-fd-accent-foreground data-[state=active]:text-fd-primary [&_svg]:size-3.5", props.className),
children: [/* @__PURE__ */ jsx("div", { className: "absolute inset-x-2 bottom-0 h-px group-data-[state=active]:bg-fd-primary" }), children]
});
}
function CodeBlockTab(props) {
return /* @__PURE__ */ jsx(TabsContent, { ...props });
}
//#endregion
export { CodeBlock, CodeBlockTab, CodeBlockTabs, CodeBlockTabsList, CodeBlockTabsTrigger, Pre };
//# sourceMappingURL=codeblock.js.map