diff --git a/docs/app/components/docs/sidebar.tsx b/docs/app/components/docs/sidebar.tsx index 391a9cdb..794e3556 100644 --- a/docs/app/components/docs/sidebar.tsx +++ b/docs/app/components/docs/sidebar.tsx @@ -103,7 +103,9 @@ function NavLink({ node, current }: { node: NavNode; current: string }) { ); } -/** A subsection with children — collapsible, auto-opens around the active page. */ +/** A subsection with children — collapsible at any depth, auto-opens around the + * active page. The label itself toggles (same behaviour as a top-level group), + * so a subsection-of-a-subsection expands/collapses on click just like its parent. */ function NavSection({ node, current }: { node: NavNode; current: string }) { const active = node.children?.some((c) => containsHref(c, current)) ?? false; const [open, setOpen] = useState(active); @@ -112,15 +114,21 @@ function NavSection({ node, current }: { node: NavNode; current: string }) { setOpen(true); } }, [active]); + const toggle = () => setOpen((o) => !o); return (
- - setOpen((o) => !o)} - title={node.title} - /> + +
{open ? : null}
@@ -189,25 +197,44 @@ function NavGroup({ group, current }: { group: NavNode; current: string }) { ); } -export function Sidebar({ onSearch }: { onSearch?: () => void }) { +export function Sidebar({ + onSearch, + open = false, + onClose, +}: { + onSearch?: () => void; + /** Drawer open state — only affects the mobile (≤860px) overlay layout. */ + open?: boolean; + onClose?: () => void; +}) { const { pathname } = useLocation(); const current = pathname.replace(/\/$/, "") || "/"; const sdk = useActiveSdk(); return ( - + <> + {/* Backdrop sits under the drawer on mobile; tapping it closes the menu. */} + + + + + ); } diff --git a/docs/app/components/landing/demo-modal.tsx b/docs/app/components/landing/demo-modal.tsx new file mode 100644 index 00000000..30afcd54 --- /dev/null +++ b/docs/app/components/landing/demo-modal.tsx @@ -0,0 +1,192 @@ +import { useEffect, useRef, useState } from "react"; +import { useThemeMode } from "@/lib/theme"; + +/** A live demo the finder can open: the embed id + a human title for the bar. */ +export interface DemoTarget { + /** Demo id understood by `demos/interactive.html?embed=` (e.g. "ratelimit"). */ + id: string; + /** Title shown in the modal bar. */ + title: string; +} + +function prefersReducedMotion(): boolean { + if (typeof window === "undefined") return false; + return Boolean( + window.matchMedia?.("(prefers-reduced-motion: reduce)").matches, + ); +} + +const PLAY_ICON = ( + +); + +const CLOSE_ICON = ( + +); + +/** + * Centered overlay that opens a finder scenario's live demo in an `