import type {SandpackInitMode} from "@codesandbox/sandpack-react"; import * as React from "react"; import { FileTabs, CodeEditor, useSandpack, useActiveCode, SandpackStack, } from "@codesandbox/sandpack-react"; import {Button} from "@nextui-org/react"; import scrollIntoView from "scroll-into-view-if-needed"; import {clsx} from "@nextui-org/shared-utils"; import {getId} from "./utils"; import {Decorators} from "./types"; export interface CodeViewerProps { showTabs?: boolean; showLineNumbers?: boolean; /** * Provides a way to draw or style a piece of the content. */ decorators?: Decorators; code?: string; wrapContent?: boolean; defaultExpanded?: boolean; /** * This provides a way to control how some components are going to * be initialized on the page. The CodeEditor and the Preview components * are quite expensive and might overload the memory usage, so this gives * a certain control of when to initialize them. */ initMode?: SandpackInitMode; containerRef?: React.RefObject; } const INITIAL_HEIGHT = "200px"; export const SandpackCodeViewer = React.forwardRef( ( { showTabs, code: propCode, decorators, initMode, defaultExpanded = false, showLineNumbers, wrapContent, containerRef, }, ref, ) => { const {sandpack} = useSandpack(); const {code} = useActiveCode(); const {activeFile} = sandpack; const [isExpanded, setIsExpanded] = React.useState(defaultExpanded); const id = React.useId(); // hack to make sure we re-render the code editor and change current file // TODO: open an issue on sandpack-react const [internalKey, setInternalKey] = React.useState(() => id); const lineCountRef = React.useRef<{[key: string]: number}>({}); if (!lineCountRef.current[activeFile]) { lineCountRef.current[activeFile] = code.split("\n").length; } const shouldShowTabs = showTabs ?? sandpack.visibleFilesFromProps.length > 1; const lineCount = lineCountRef.current[activeFile]; const isExpandable = lineCount > 7 || isExpanded; const isAppFile = activeFile.includes("App"); React.useEffect(() => { if (containerRef && containerRef?.current !== null && isExpandable) { containerRef.current.style.height = INITIAL_HEIGHT; } }, [containerRef]); React.useEffect(() => { setInternalKey(getId()); }, [propCode, code]); React.useEffect(() => { if (defaultExpanded && containerRef && containerRef?.current !== null) { const container = containerRef?.current; container.style.height = "auto"; } }, [defaultExpanded]); const handleExpand = () => { const nextIsExpanded = !isExpanded; setIsExpanded(nextIsExpanded); if (containerRef && containerRef?.current !== null) { const container = containerRef?.current; if (nextIsExpanded) { container.style.height = "auto"; } else { container.style.height = INITIAL_HEIGHT; scrollIntoView(container, { behavior: "smooth", scrollMode: "if-needed", block: "center", }); } } }; return ( <>
{shouldShowTabs ? : null}
{isExpandable && (
)} ); }, ); SandpackCodeViewer.displayName = "SandpackCodeViewer"; export default SandpackCodeViewer;