jjb-lc-designable
Version:
基于alibaba-designable源码二次封装的表单设计器。
252 lines (251 loc) • 8.31 kB
JavaScript
function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
import React, { useState, useRef, useEffect } from 'react';
import Editor, { loader } from 'jjb-lc-plugin-monaco-editor/react';
import { TextWidget, IconWidget, usePrefix, useTheme } from 'jjb-lc-designable/react';
// @ts-ignore
import { Tooltip } from 'antd';
import { parseExpression, parse } from '@babel/parser';
import { uid } from 'jjb-lc-designable/shared';
import { format } from './format';
import cls from 'classnames';
import './styles.less';
import './config';
import { initMonaco } from './config';
export const MonacoInput = ({
className,
language,
defaultLanguage,
width,
helpLink,
helpCode,
helpCodeViewWidth,
height,
onMount,
onChange,
...props
}) => {
const [loaded, setLoaded] = useState(false);
const theme = useTheme();
const valueRef = useRef('');
const validateRef = useRef(null);
const submitRef = useRef(null);
const declarationRef = useRef([]);
const extraLibRef = useRef(null);
const monacoRef = useRef();
const editorRef = useRef();
const computedLanguage = useRef(language || defaultLanguage);
const realLanguage = useRef('');
const unmountedRef = useRef(false);
const changedRef = useRef(false);
const uidRef = useRef(uid());
const prefix = usePrefix('monaco-input');
const input = props.value || props.defaultValue;
useEffect(() => {
unmountedRef.current = false;
initMonaco();
return () => {
if (extraLibRef.current) {
extraLibRef.current.dispose();
}
unmountedRef.current = true;
};
}, []);
useEffect(() => {
if (monacoRef.current && props.extraLib) {
updateExtraLib();
}
}, [props.extraLib]);
const updateExtraLib = () => {
if (extraLibRef.current) {
extraLibRef.current.dispose();
}
extraLibRef.current = monacoRef.current.languages.typescript.typescriptDefaults.addExtraLib(props.extraLib, `${uidRef.current}.d.ts`);
};
const isFileLanguage = () => {
const lang = computedLanguage.current;
return lang === 'javascript' || lang === 'typescript';
};
const isExpLanguage = () => {
const lang = computedLanguage.current;
return lang === 'javascript.expression' || lang === 'typescript.expression';
};
const renderHelper = () => {
const getHref = () => {
if (typeof helpLink === 'string') return helpLink;
if (isFileLanguage()) {
return 'https://developer.mozilla.org/zh-CN/docs/Web/JavaScript';
}
if (isExpLanguage()) {
return 'https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators';
}
};
if (helpLink === false) return null;
const href = getHref();
return href && /*#__PURE__*/React.createElement(Tooltip, {
title: /*#__PURE__*/React.createElement(TextWidget, {
token: "SettingComponents.MonacoInput.helpDocument"
})
}, /*#__PURE__*/React.createElement("div", {
className: prefix + '-helper'
}, /*#__PURE__*/React.createElement("a", {
target: "_blank",
href: href,
rel: "noreferrer"
}, /*#__PURE__*/React.createElement(IconWidget, {
infer: "Help"
}))));
};
const onMountHandler = (editor, monaco) => {
editorRef.current = editor;
monacoRef.current = monaco;
onMount?.(editor, monaco);
const model = editor.getModel();
const currentValue = editor.getValue();
model['getDesignerLanguage'] = () => computedLanguage.current;
if (currentValue) {
format(computedLanguage.current, currentValue).then(content => {
editor.setValue(content);
setLoaded(true);
}).catch(() => {
setLoaded(true);
});
} else {
setLoaded(true);
}
if (props.extraLib) {
updateExtraLib();
}
editor.onDidChangeModelContent(() => {
onChangeHandler(editor.getValue());
});
};
const submit = () => {
clearTimeout(submitRef.current);
submitRef.current = setTimeout(() => {
onChange?.(valueRef.current);
}, 1000);
};
const validate = () => {
if (realLanguage.current === 'typescript') {
clearTimeout(validateRef.current);
validateRef.current = setTimeout(() => {
try {
if (valueRef.current) {
if (isFileLanguage()) {
parse(valueRef.current, {
sourceType: 'module',
plugins: ['typescript', 'jsx']
});
} else if (isExpLanguage()) {
parseExpression(valueRef.current, {
plugins: ['typescript', 'jsx']
});
}
}
monacoRef.current.editor.setModelMarkers(editorRef.current.getModel(), computedLanguage.current, []);
declarationRef.current = editorRef.current.deltaDecorations(declarationRef.current, [{
range: new monacoRef.current.Range(1, 1, 1, 1),
options: {}
}]);
submit();
} catch (e) {
declarationRef.current = editorRef.current.deltaDecorations(declarationRef.current, [{
range: new monacoRef.current.Range(e.loc.line, e.loc.column, e.loc.line, e.loc.column),
options: {
isWholeLine: true,
glyphMarginClassName: 'monaco-error-highline'
}
}]);
monacoRef.current.editor.setModelMarkers(editorRef.current.getModel(), computedLanguage.current, [{
code: '1003',
severity: 8,
startLineNumber: e.loc.line,
startColumn: e.loc.column,
endLineNumber: e.loc.line,
endColumn: e.loc.column,
message: e.message
}]);
}
}, 240);
} else {
submit();
declarationRef.current = editorRef.current.deltaDecorations(declarationRef.current, [{
range: new monacoRef.current.Range(1, 1, 1, 1),
options: {}
}]);
}
};
const onChangeHandler = value => {
changedRef.current = true;
valueRef.current = value;
validate();
};
computedLanguage.current = language || defaultLanguage;
realLanguage.current = /(?:javascript|typescript)/gi.test(computedLanguage.current) ? 'typescript' : computedLanguage.current;
const renderHelpCode = () => {
if (!helpCode) return null;
return /*#__PURE__*/React.createElement("div", {
className: prefix + '-view',
style: {
width: helpCodeViewWidth || '50%'
}
}, /*#__PURE__*/React.createElement(Editor, {
value: helpCode,
theme: theme === 'dark' ? 'monokai' : 'chrome-devtools',
defaultLanguage: realLanguage.current,
language: realLanguage.current,
options: {
...props.options,
lineNumbers: 'off',
readOnly: true,
glyphMargin: false,
folding: false,
lineDecorationsWidth: 0,
lineNumbersMinChars: 0,
minimap: {
enabled: false
},
tabSize: 2,
smoothScrolling: true,
scrollbar: {
verticalScrollbarSize: 5,
horizontalScrollbarSize: 5,
alwaysConsumeMouseWheel: false
}
},
width: "100%",
height: "100%"
}));
};
return /*#__PURE__*/React.createElement("div", {
className: cls(prefix, className, {
loaded
}),
style: {
width,
height
}
}, renderHelper(), /*#__PURE__*/React.createElement("div", {
className: prefix + '-view'
}, /*#__PURE__*/React.createElement(Editor, _extends({}, props, {
theme: theme === 'dark' ? 'monokai' : 'chrome-devtools',
defaultLanguage: realLanguage.current,
language: realLanguage.current,
options: {
glyphMargin: true,
...props.options,
tabSize: 2,
smoothScrolling: true,
scrollbar: {
verticalScrollbarSize: 5,
horizontalScrollbarSize: 5,
alwaysConsumeMouseWheel: false
}
},
value: input,
width: "100%",
height: "100%",
onMount: onMountHandler
}))), renderHelpCode());
};
MonacoInput.loader = loader;