/* eslint no-underscore-dangle: 0 */
import React, { useRef, useEffect, useState } from 'react';
import { UnControlled as CodeMirrorEditor } from 'react-codemirror2';
import { Editor } from 'codemirror';
import { useMedia } from 'react-use';
import classNames from 'classnames';
import { Result } from 'antd';
import debounce from 'lodash/debounce';
import {
useTranslation,
withTranslation,
WithTranslation,
} from 'react-i18next';
import { transform } from '@babel/standalone';
import SplitPane from 'react-split-pane';
import Toolbar from './Toolbar';
import styles from './PlayGround.module.less';
export interface PlayGroundProps {
source: string;
babeledSource: string;
absolutePath?: string;
relativePath?: string;
screenshot?: string;
recommended?: boolean;
filename: string;
title?: string;
location?: Location;
playground?: {
container?: string;
playgroundDidMount?: string;
playgroundWillUnmount?: string;
dependencies?: {
[key: string]: string;
};
htmlCodeTemplate?: string;
};
}
const execute = debounce(
(
code: string,
node: HTMLDivElement,
exampleContainer: string | undefined,
) => {
const script = document.createElement('script');
script.innerHTML = `
try {
${code}
} catch(e) {
if (window.__reportErrorInPlayGround) {
window.__reportErrorInPlayGround(e);
}
}
`;
// eslint-disable-next-line no-param-reassign
node.innerHTML = exampleContainer || '
';
node!.appendChild(script);
},
300,
);
const PlayGround: React.FC = ({
source,
babeledSource,
relativePath = '',
playground = {},
location,
title = '',
}) => {
const { t } = useTranslation();
const playgroundNode = useRef(null);
const cmInstance = useRef();
const [error, setError] = useState();
const [compiledCode, updateCompiledCode] = useState(babeledSource);
const [currentSourceCode, updateCurrentSourceCode] = useState(source);
if (typeof window !== 'undefined') {
// @ts-ignore
window.__reportErrorInPlayGround = (e: Error) => {
console.error(e); // eslint-disable-line no-console
setError(e);
};
}
const fullscreenNode = useRef(null);
const [isFullScreen, updateIsFullScreen] = useState(false);
const toggleFullscreen = () => {
updateIsFullScreen(!isFullScreen);
if (fullscreenNode.current) {
if (!isFullScreen && !document.fullscreenElement) {
fullscreenNode.current.requestFullscreen();
} else if (document.exitFullscreen) {
document.exitFullscreen();
}
}
};
const executeCode = () => {
if (!compiledCode || !playgroundNode || !playgroundNode.current) {
return;
}
execute(compiledCode, playgroundNode.current, playground.container);
};
useEffect(() => {
executeCode();
}, [compiledCode, error]);
useEffect(() => {
if (playground.playgroundDidMount) {
// eslint-disable-next-line no-new-func
new Function(playground.playgroundDidMount)();
}
return () => {
if (playground.playgroundWillUnmount) {
// eslint-disable-next-line no-new-func
new Function(playground.playgroundWillUnmount)();
}
};
}, []);
// 统一增加对 insert-css 的使用注释
const replacedSource = source.replace(
/^insertCss\(/gm,
`// 我们用 insert-css 演示引入自定义样式
// 推荐将样式添加到自己的样式文件中
// 若拷贝官方代码,别忘了 npm install insert-css
insertCss(`,
);
const editor = (
{
updateCurrentSourceCode(value);
try {
const { code } = transform(value, {
filename: relativePath,
presets: ['react', 'typescript', 'es2015', 'stage-3'],
plugins: ['transform-modules-umd'],
});
updateCompiledCode(code);
} catch (e) {
console.error(e); // eslint-disable-line no-console
setError(e);
return;
}
setError(null);
}}
editorDidMount={instance => {
cmInstance.current = instance;
}}
/>
);
const fileExtension =
relativePath.split('.')[relativePath.split('.').length - 1] || 'js';
const isWide = useMedia('(min-width: 767.99px)', true);
return (
{error ? (
{error && error.message}}
/>
) : (
)}
);
};
class ErrorHandlerPlayGround extends React.Component<
PlayGroundProps & WithTranslation,
{ error?: Error }
> {
state = {
error: undefined,
};
static getDerivedStateFromError(error: Error) {
// 更新 state 使下一次渲染能够显示降级后的 UI
return { error };
}
render() {
const { t } = this.props;
const { error } = this.state;
if (error) {
// 你可以自定义降级后的 UI 并渲染
return (
{error && (error as any).message}}
/>
);
}
return ;
}
}
export default withTranslation()(ErrorHandlerPlayGround);