import * as React from 'react'; import { useEffect, useRef, FC } from 'react'; import { useConfig } from 'docz'; import { Controlled as BaseCodeMirror } from 'react-codemirror2'; import PerfectScrollbar from 'react-perfect-scrollbar'; import styled from 'styled-components'; import { get } from '../Playground/theme'; import { ScrollbarStyles } from './ps-scrollbar'; import * as themes from './themes'; import 'codemirror/mode/markdown/markdown'; import 'codemirror/mode/javascript/javascript'; import 'codemirror/mode/jsx/jsx'; import 'codemirror/mode/css/css'; import 'codemirror/addon/edit/matchbrackets'; import 'codemirror/addon/edit/closetag'; import 'codemirror/addon/fold/xml-fold'; const Scrollbar = styled(PerfectScrollbar)` overflow: auto; position: relative; max-height: ${p => 25 * p.linesToScroll}px; .ps__rail-y { z-index: 9; opacity: 0.4; } `; const preStyles = get('styles.pre'); const EditorStyled = styled(BaseCodeMirror)` ${themes.light}; position: relative; flex: 1; .CodeMirror { max-width: 100%; height: 100%; } .CodeMirror-gutters { left: 1px !important; } .CodeMirror-lines { padding: 10px 0; box-sizing: content-box; } .CodeMirror-line { padding: 0 10px; } .CodeMirror-linenumber { padding: 0 7px 0 5px; } &, .CodeMirror pre { line-height: 20px; ${preStyles}; } `; const scrollbarOpts = { wheelSpeed: 2, wheelPropagation: true, minScrollbarLength: 20, suppressScrollX: true, }; const noCurrent = (val: any) => !val || !val.current; const CodeMirror: FC = props => { const { themeConfig } = useConfig(); const editor = useRef(null); const forceUpdateEditorTimeout = useRef(0); const previousEditor = useRef(0); const linesToScroll = themeConfig.linesToScrollEditor || 14; const editorProps = { ...props, editorDidMount: (codemirror: any) => { props.editorDidMount && props.editorDidMount(codemirror); editor.current = codemirror; }, }; const refreshCodeMirror = () => { if (noCurrent(editor)) return; editor.current.refresh(); }; const clearForceUpdateCodeMirror = () => { if (noCurrent(forceUpdateEditorTimeout)) return; clearTimeout(forceUpdateEditorTimeout.current); }; const forceUpdateCodeMirror = () => { if (noCurrent(editor)) return; clearForceUpdateCodeMirror(); forceUpdateEditorTimeout.current = setTimeout(() => { const currentHeight = editor.current.getScrollInfo().height || 0; const hasNoHeight = currentHeight <= 0; // Don't refresh if no height (CodeMirror is not visible) or // Don't refresh if same height if (hasNoHeight || previousEditor === currentHeight) return; refreshCodeMirror(); previousEditor.current = editor.current.getScrollInfo().height || 0; }); }; useEffect(() => { forceUpdateCodeMirror(); return () => clearForceUpdateCodeMirror(); }, []); return ( ); }; export default CodeMirror;