blob: 1f283a9def8d0c03901084386af5bd6b593ac446 [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import * as React from "react";
import { useEffect, useRef, SFC } from "react";
import { useConfig } from "../../../../docz-lib/docz/dist";
import { Controlled as BaseCodeMirror } from "react-codemirror2";
import PerfectScrollbar from "react-perfect-scrollbar";
import styled from "styled-components";
import { get } from "~utils/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.dark};
${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 {
${preStyles};
}
`;
const scrollbarOpts = {
wheelSpeed: 2,
wheelPropagation: true,
minScrollbarLength: 20,
suppressScrollX: true
};
const noCurrent = (val: any) => !val || !val.current;
const CodeMirror: SFC<any> = props => {
const { themeConfig } = useConfig();
const editor = useRef < any > 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 (
<React.Fragment>
<ScrollbarStyles />
<Scrollbar options={scrollbarOpts} linesToScroll={linesToScroll}>
<EditorStyled {...editorProps} />
</Scrollbar>
</React.Fragment>
);
};
export default CodeMirror;