stack/docs/src/components/mdx/dynamic-code-block.tsx
Madison 8c805a8b8f
[Docs][Util][Content] - refactor docs to single source (#919)
<!--

Make sure you've read the CONTRIBUTING.md guidelines:
https://github.com/stack-auth/stack-auth/blob/dev/CONTRIBUTING.md

-->

Removes Platform selection, moves docs to single /content folder and no
longer gens docs. Only API docs are generated here.

<!-- RECURSEML_SUMMARY:START -->
## High-level PR Summary
This PR makes significant changes to the documentation structure by
removing platform-specific content organization and consolidating docs
into a single `/content` folder. The primary goal is to simplify the
documentation architecture by eliminating the platform-specific routing
(Next.js, React, JavaScript, Python) and instead organizing content by
topic (guides, SDK, components) regardless of platform. The PR removes
platform selection functionality, platform-specific navigation, and the
automatic generation of platform-specific documentation pages. It
introduces a new docs tree filtering system that organizes content by
section rather than by platform. These changes should make the
documentation more maintainable and easier to navigate while focusing on
the content itself rather than platform-specific variations.

⏱️ Estimated Review Time: 30-90 minutes

<details>
<summary>💡 Review Order Suggestion</summary>

| Order | File Path |
|-------|-----------|
| 1 | `docs/package.json` |
| 2 | `docs/src/lib/docs-tree.ts` |
| 3 | `docs/src/lib/navigation-utils.ts` |
| 4 | `docs/src/components/homepage/iconHover.tsx` |
| 5 | `docs/src/components/sdk/overview.tsx` |
| 6 | `docs/src/components/layouts/shared/section-utils.ts` |
| 7 | `docs/src/components/layout/custom-search-dialog.tsx` |
| 8 | `docs/src/app/api/search/route.ts` |
| 9 | `docs/src/app/docs/[[...slug]]/page.tsx` |
| 10 | `docs/src/components/layouts/docs-header-wrapper.tsx` |
| 11 | `docs/src/components/layouts/docs-layout-router.tsx` |
| 12 | `docs/src/components/layouts/docs.tsx` |
| 13 | `package.json` |
</details>



[![Need help? Join our
Discord](https://img.shields.io/badge/Need%20help%3F%20Join%20our%20Discord-5865F2?style=plastic&logo=discord&logoColor=white)](https://discord.gg/n3SsVDAW6U)

<!-- RECURSEML_SUMMARY:END -->

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Documentation**
* Added many new guides (auth providers, OAuth, JWT, API keys, emails,
webhooks, orgs/teams, permissions, onboarding, customization), expanded
SDK & component reference pages, examples, and navigation metadata.
* Switched docs to a simpler section-based, platform-agnostic structure
and improved getting-started and production checklists.

* **Developer Experience**
* Enhanced docs UX: improved code-example UI with platform/framework
selectors, theme-aware highlighted code blocks, image zoom, and a
centralized code-sample registry.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Konstantin Wohlwend <n2d4xc@gmail.com>
2025-10-20 12:42:46 -05:00

118 lines
3.8 KiB
TypeScript

'use client';
import { Code } from "lucide-react";
import { useEffect, useState } from "react";
import { useCodeOverlay } from "../../hooks/use-code-overlay";
import { cn } from "../../lib/cn";
import { useSidebar } from "../layouts/sidebar-context";
type DynamicCodeblockProps = {
code: string,
language?: string,
title?: string,
}
/**
* DynamicCodeblock - Trigger component for code overlay
*
* Auto-opens the code overlay and shows a floating "View Code" button when closed.
* The actual code rendering is handled by DynamicCodeblockOverlay (global in layout).
*/
export function DynamicCodeblock({
code,
language = 'tsx',
title = "Code Example"
}: DynamicCodeblockProps) {
const [hasInitialized, setHasInitialized] = useState(false);
const [windowWidth, setWindowWidth] = useState(0);
const [isNearBottom, setIsNearBottom] = useState(false);
const { openOverlay, isOpen } = useCodeOverlay();
const sidebarContext = useSidebar();
const isMainSidebarCollapsed = sidebarContext?.isMainSidebarCollapsed ?? false;
// Handle window resize for responsive button positioning
useEffect(() => {
const updateWindowWidth = () => {
if (typeof window !== 'undefined') {
setWindowWidth(window.innerWidth);
}
};
updateWindowWidth();
window.addEventListener('resize', updateWindowWidth);
return () => window.removeEventListener('resize', updateWindowWidth);
}, []);
// Handle scroll to fade button when near bottom (avoid overlapping with next/prev buttons)
useEffect(() => {
const handleScroll = () => {
if (typeof window === 'undefined') return;
const scrollTop = window.scrollY || document.documentElement.scrollTop;
const windowHeight = window.innerHeight;
const documentHeight = document.documentElement.scrollHeight;
const distanceFromBottom = documentHeight - (scrollTop + windowHeight);
setIsNearBottom(distanceFromBottom < 200);
};
handleScroll();
window.addEventListener('scroll', handleScroll, { passive: true });
return () => window.removeEventListener('scroll', handleScroll);
}, []);
// Auto-open overlay on mount (only once)
useEffect(() => {
if (code && !hasInitialized) {
const timer = setTimeout(() => {
openOverlay(code, language, title);
setHasInitialized(true);
}, 100);
return () => clearTimeout(timer);
}
}, [code, language, title, openOverlay, hasInitialized]);
// Update overlay content when code/props change (if overlay is already open)
useEffect(() => {
if (code && hasInitialized && isOpen) {
openOverlay(code, language, title);
}
}, [code, language, title, openOverlay, hasInitialized, isOpen]);
// Show floating "View Code" button when overlay is closed
if (!isOpen) {
return (
<button
onClick={() => openOverlay(code, language, title)}
className={cn(
"fixed bottom-6 z-30",
"flex items-center gap-1.5 px-3 py-2",
"bg-fd-primary text-fd-primary-foreground",
"rounded-full shadow-lg",
"hover:scale-105 active:scale-95",
"transition-all duration-300",
"border border-fd-primary/20"
)}
style={{
// On mobile: center normally (50%)
// On desktop: center relative to content area, accounting for sidebar offset
left: windowWidth < 768 ? '50%' : `calc(50% + ${isMainSidebarCollapsed ? '2rem' : '8rem'})`,
transform: 'translateX(-50%)',
// Fade out when near bottom to avoid interfering with next/prev buttons
opacity: isNearBottom ? 0 : 1,
pointerEvents: isNearBottom ? 'none' : 'auto'
}}
title="View Code Example"
>
<Code className="h-3.5 w-3.5" />
<span className="text-xs font-medium">View Code</span>
</button>
);
}
return null;
}