import * as React from 'react'; import CodeSandboxer from 'react-codesandboxer'; import { ComponentSourceManagerLanguage } from '../../ComponentSourceManager'; import { appTemplate } from './indexTemplates'; import createPackageJson from './createPackageJson'; export enum CodeSandboxState { Default = 'DEFAULT', Loading = 'LOADING', Success = 'SUCCESS', } type ComponentControlsCodeSandboxProps = { children: (state: CodeSandboxState, onClick?: (e: React.SyntheticEvent) => void) => React.ReactNode; exampleCode: string; exampleLanguage: ComponentSourceManagerLanguage; exampleName: string; }; type ComponentControlsCodeSandboxState = { exampleCode: string; examplePath: string; state: CodeSandboxState; sandboxUrl: string; }; class ComponentControlsCodeSandbox extends React.Component< ComponentControlsCodeSandboxProps, ComponentControlsCodeSandboxState > { state = { exampleCode: '', examplePath: '', sandboxUrl: '', state: CodeSandboxState.Default, }; codeSandboxerRef = React.createRef<{ deployToCSB: Function }>(); static getDerivedStateFromProps( props: ComponentControlsCodeSandboxProps, state: ComponentControlsCodeSandboxState, ): Partial { const shouldKeepState = props.exampleCode === state.exampleCode; return { exampleCode: props.exampleCode, examplePath: props.exampleLanguage === 'ts' ? '/example.tsx' : '/example.js', sandboxUrl: shouldKeepState ? state.sandboxUrl : '', state: shouldKeepState ? state.state : CodeSandboxState.Default, }; } handleDeploy = (embedUrl: string, sandboxId: string) => { const { examplePath } = this.state; const sandboxUrl = `https://codesandbox.io/s/${sandboxId}?module=${examplePath}`; this.setState({ sandboxUrl, state: CodeSandboxState.Success }); }; handleClick = (e: React.SyntheticEvent) => { const { sandboxUrl, state } = this.state; e.preventDefault(); if (state === CodeSandboxState.Default) { this.codeSandboxerRef.current.deployToCSB(e); this.setState({ state: CodeSandboxState.Loading }); return; } if (state === CodeSandboxState.Success) { window.open(sandboxUrl); } }; render() { const { children, exampleLanguage, exampleCode, exampleName } = this.props; const { examplePath, state } = this.state; const main = exampleLanguage === 'ts' ? 'index.tsx' : 'index.js'; const template = exampleLanguage === 'ts' ? 'create-react-app-typescript' : 'create-react-app'; return ( <> {/* CodeSandboxer captures ref from children and binds an event handler directly to DOM, this hack allows to avoid this behavior */} {() => null} {children(state, this.handleClick)} ); } } export default ComponentControlsCodeSandbox;