-
- ({
- key,
- label,
- kind,
- }))}
- />
-
-
-
-
- {languages.map((l, idx) => (
-
- {/* Cast to any to align with shiki BundledLanguage without importing types here */}
-
-
- {l.code}
-
-
-
- ))}
-
-
-
-
- Language example toggled
-
+
+ ({
+ key,
+ label,
+ kind,
+ }))}
+ panels={panels}
+ />
diff --git a/apps/marketing/src/components/CodeExampleClient.tsx b/apps/marketing/src/components/CodeExampleClient.tsx
new file mode 100644
index 00000000..4c9b554b
--- /dev/null
+++ b/apps/marketing/src/components/CodeExampleClient.tsx
@@ -0,0 +1,70 @@
+"use client";
+
+import { useLayoutEffect, useRef, useState, type ReactNode } from "react";
+import { LangToggle } from "./CodeLangToggle";
+
+type LangItem = {
+ key: string;
+ label: string;
+ kind: string;
+};
+
+export function CodeExampleClient({
+ languages,
+ panels,
+}: {
+ languages: LangItem[];
+ panels: Record
;
+}) {
+ const [activeLang, setActiveLang] = useState(languages[0]?.key ?? "ts");
+ const contentRef = useRef(null);
+ const [height, setHeight] = useState();
+
+ useLayoutEffect(() => {
+ const el = contentRef.current;
+ if (!el) return;
+
+ const measure = () => setHeight(el.scrollHeight);
+ measure();
+
+ const observer = new ResizeObserver(measure);
+ observer.observe(el);
+ return () => observer.disconnect();
+ }, [activeLang]);
+
+ return (
+ <>
+
+
+
+
+
+
+
+ {languages.map((l) => (
+
+ {panels[l.key]}
+
+ ))}
+
+
+
+
+
+ Language example toggled
+
+ >
+ );
+}
+
+export default CodeExampleClient;
diff --git a/apps/marketing/src/components/CodeLangToggle.tsx b/apps/marketing/src/components/CodeLangToggle.tsx
index 08d04065..53528ce7 100644
--- a/apps/marketing/src/components/CodeLangToggle.tsx
+++ b/apps/marketing/src/components/CodeLangToggle.tsx
@@ -1,6 +1,6 @@
"use client";
-import { useEffect, useState } from "react";
+import { useState } from "react";
import Image from "next/image";
import { Button } from "@usesend/ui/src/button";
@@ -11,35 +11,14 @@ type LangItem = {
};
export function LangToggle({
- containerId,
languages,
- defaultLang,
+ active,
+ onActiveChange,
}: {
- containerId: string;
languages: LangItem[];
- defaultLang: string;
+ active: string;
+ onActiveChange: (key: string) => void;
}) {
- const [active, setActive] = useState(defaultLang);
-
- useEffect(() => {
- const container = document.getElementById(containerId);
- if (!container) return;
-
- const slots = Array.from(
- container.querySelectorAll("[data-lang-slot]")
- );
- for (const el of slots) {
- const key = el.getAttribute("data-lang-slot");
- if (key === active) {
- el.classList.remove("hidden");
- el.classList.add("block");
- } else {
- el.classList.add("hidden");
- el.classList.remove("block");
- }
- }
- }, [active, containerId]);
-
return (
{languages.map((l) => (
@@ -54,7 +33,7 @@ export function LangToggle({
: "border-input")
}
aria-pressed={active === l.key}
- onClick={() => setActive(l.key)}
+ onClick={() => onActiveChange(l.key)}
>
{l.label}