blob: 727df9fc5e503da04fcaa91abbec6bdf6253d0e5 [file]
import * as React from 'react';
import {
FileTabs,
CodeEditor,
useSandpack,
useActiveCode,
SandpackStack
} from '@codesandbox/sandpack-react';
import type { SandpackInitMode } from '@codesandbox/sandpack-react';
import scrollIntoView from 'scroll-into-view-if-needed';
import { Decorators } from './types';
import { getId } from '@utils/collections';
import { Box } from '@primitives';
import { StyledShoreMoreButton } from './styles';
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;
/**
* 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<HTMLDivElement>;
}
/**
* @category Components
*/
const SandpackCodeViewer = React.forwardRef<any, CodeViewerProps>(
(
{
showTabs,
code: propCode,
decorators,
initMode,
showLineNumbers,
wrapContent,
containerRef
},
ref
) => {
const { sandpack } = useSandpack();
const { code } = useActiveCode();
const { activePath } = sandpack;
const [isExpanded, setIsExpanded] = React.useState(false);
// hack to make sure we re-render the code editor and chenge current file
// TODO: open an issue on sandpack-react
const [internalKey, setInternalKey] = React.useState(getId());
const lineCountRef = React.useRef<{ [key: string]: number }>({});
if (!lineCountRef.current[activePath]) {
lineCountRef.current[activePath] = code.split('\n').length;
}
const shouldShowTabs = showTabs ?? sandpack.openPaths.length > 1;
const lineCount = lineCountRef.current[activePath];
const isExpandable = lineCount > 12 || isExpanded;
React.useEffect(() => {
if (containerRef && containerRef?.current !== null && isExpandable) {
containerRef.current.style.height = '350px';
}
}, [containerRef]);
React.useEffect(() => {
setInternalKey(getId());
}, [propCode, code]);
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 = '350px';
scrollIntoView(container, {
behavior: 'smooth',
scrollMode: 'if-needed',
block: 'center'
});
}
}
};
return (
<SandpackStack>
{shouldShowTabs ? <FileTabs /> : null}
<CodeEditor
readOnly
key={internalKey}
ref={ref}
code={propCode || code}
decorators={decorators}
filePath={sandpack.activePath}
initMode={initMode || sandpack.initMode}
showLineNumbers={showLineNumbers}
showReadOnly={false}
wrapContent={wrapContent}
/>
{isExpandable && (
<Box css={{ py: '$3', pl: 'var(--sp-space-4)' }}>
<StyledShoreMoreButton onClick={handleExpand}>
{isExpanded ? 'Show less' : 'Show more'}
</StyledShoreMoreButton>
</Box>
)}
</SandpackStack>
);
}
);
SandpackCodeViewer.displayName = 'SandpackCodeViewer';
export default SandpackCodeViewer;