2021/02/28更
此次更新主要增加了給編輯器鼠標(biāo)焦點處設(shè)置值方法
/**
* 給編輯器設(shè)置值
* @param value 需要設(shè)置的值
*/
const handleSetEditorVal = (value: string): void => {
if (!value) return;
// 為所選取的值賦值到編輯器中
if (editorInstance.current && value) {
const selection = editorInstance?.current?.getSelection?.();
const range = new _monaco.Range(
selection.startLineNumber,
selection.startColumn,
selection.endLineNumber,
selection.endColumn
);
const id = { major: 1, minor: 1 };
const op = { identifier: id, range, text: value, forceMoveMarkers: true };
editorInstance.current.executeEdits('', [op]);
editorInstance.current.focus();
}
};
開始
最近產(chǎn)品上用到了代碼編輯器,因為技術(shù)棧使用的是react, 找了許久經(jīng)過甄選最后決定使用monaco-editor的react封裝版本:react-monaco-edotor案站,下面是他的簡單介紹:
經(jīng)過很多前輩大佬的努力砂缩,react-monaco-edotor橫空出世归敬,對于使用react技術(shù)棧的人是一個再好不過的好消息了惨寿,介紹完畢我們講講如何使用吧香璃。
使用方法:
yarn add monaco-editor -D
yarn add react-monaco-editor -D
yarn add monaco-editor-webpack-plugin -D
安裝好依賴包再去webpack-config中配置相關(guān)依賴:
const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin');
...
plugins: [
new MonacoWebpackPlugin(['apex', 'azcli', 'bat', 'clojure', 'coffee', 'cpp', 'csharp', 'csp', 'css', 'dockerfile', 'fsharp', 'go', 'handlebars', 'html', 'ini', 'java', 'javascript', 'json', 'less', 'lua', 'markdown', 'msdax', 'mysql', 'objective', 'perl', 'pgsql', 'php', 'postiats', 'powerquery', 'powershell', 'pug', 'python', 'r', 'razor', 'redis', 'redshift', 'ruby', 'rust', 'sb', 'scheme', 'scss', 'shell',
'solidity', 'sql', 'st', 'swift', 'typescript', 'vb', 'xml', 'yaml']),
...
]
注意:自定義提示時一定需設(shè)置 language: 'plaintext' 自定義文本, 并且在monaco.languages.registerCompletionItemProvider中使用深拷貝
經(jīng)過以上步驟 編輯器就能正常應(yīng)用到項目中去了和橙,使用及自定義提示的使用方法:
import React, { useRef, useEffect, forwardRef, useImperativeHandle } from 'react';
import { Icon, Tooltip } from 'choerodon-ui/pro';
import MonacoEditor from 'react-monaco-editor';
import * as _monaco from 'monaco-editor';
import styles from './index.less';
let provider = {
dispose: () => {},
};
interface IRightContent {
currentRecord: any; // fixme
handleCheck: () => Promise<boolean>;
secondRightData: (model.dataSource.BaseDataSourceField | model.dataSource.BaseDataSourceHeader)[];
}
export default forwardRef(
({ currentRecord, handleCheck, secondRightData = [] }: IRightContent, ref) => {
// 編輯器實例
const editorInstance = useRef<any>();
// 真實數(shù)據(jù)
const code: any = useRef(currentRecord ? currentRecord.formulaContent : '');
// 緩存數(shù)據(jù)
const cache: any = useRef(code.current);
// 輸入引號觸發(fā)頁面刷新
const [refresh, setRefresh] = React.useState<boolean>(false);
const monacoInstance: any = useRef();
const options = {
selectOnLineNumbers: true,
renderSideBySide: false,
};
useImperativeHandle(ref, () => ({
handleSetEditorVal,
getEditorData: () => cache.current,
}));
useEffect(
() => () => {
provider.dispose(); // 彈窗關(guān)閉后 銷毀編輯器實例
},
[]
);
/**
* 給編輯器設(shè)置值
* @param value 需要設(shè)置的值
*/
const handleSetEditorVal = (value: string): void => {
if (!value) return;
// 為所選取的值賦值到編輯器中
if (editorInstance.current && value) {
const selection = editorInstance?.current?.getSelection?.();
const range = new _monaco.Range(
selection.startLineNumber,
selection.startColumn,
selection.endLineNumber,
selection.endColumn
);
const id = { major: 1, minor: 1 };
const op = { identifier: id, range, text: value, forceMoveMarkers: true };
editorInstance.current.executeEdits('', [op]);
editorInstance.current.focus();
}
};
/**
* 編輯器change回調(diào)
* @param {String} val 當(dāng)前編輯器的值
*/
const onChangeHandle = (val: string, event: { changes: { text: any }[] }) => {
const curWord = event.changes[0].text;
if (curWord === '"') {
cache.current = val + curWord;
setRefresh(!refresh); // 刷新頁面
return;
}
cache.current = val;
};
/**
* 初始化編輯器
* @param {*} editor 編輯器實例
* @param {*} monaco
*/
interface ISuggestions {
label: string;
kind: string;
insertText: string;
detail?: string;
}
const editorDidMountHandle = (editor: any, monaco: any) => {
monacoInstance.current = monaco;
editorInstance.current = editor;
const newSecondRightFields: model.dataSource.BaseDataSourceHeader[] = [];
(secondRightData as model.dataSource.BaseDataSourceHeader[]).forEach((record) => {
if (record.fields && Array.isArray(record.fields)) {
record.fields.forEach((item: any) => {
// fixme
newSecondRightFields.push(item);
});
}
code.current = newSecondRightFields; // 數(shù)組長度永遠為1
});
// 提示項設(shè)值
provider = monaco.languages.registerCompletionItemProvider('plaintext', {
provideCompletionItems() {
const suggestions: ISuggestions[] = [];
if (code && code.current) {
code.current.forEach((record) => {
suggestions.push({
// label未寫錯 中間加空格為了隱藏大寫字段名稱 大寫字段名稱用于規(guī)避自定義提示不匹配小寫的bug
label:
record.label ||
`${record.displayName} (${
record.aliasName
}) ${''}(${record.aliasName.toUpperCase()})`, // 顯示名稱
kind: record.kind || monaco.languages.CompletionItemKind.Field, // 這里Function也可以是別的值顾彰,主要用來顯示不同的圖標(biāo)
insertText: record.insertText || record.aliasName, // 實際粘貼上的值
// detail: record.detail || `(property) ${record.aliasName}: String`,
});
});
}
[
'CASEWHEN(expression1, value1, expression2, value2, ..., else_value)',
'CONCAT(str1, str2, ...)',
'ISNULL (expression, defaultValue)',
'DATEDIFF_YEAR(startdate,enddate)',
'DATEDIFF_MONTH(startdate,enddate)',
'DATEDIFF_DAY(startdate,enddate)',
'SUM(expression)',
'AVG(expression)',
'MAX(expression)',
'MIN(expression)',
'COUNT(expression)',
'DISTINCTCOUNT(expression)',
'DISTINCTAVG(expression)',
'DISTINCTSUM(expression)',
'NOW()',
].forEach((item) => {
suggestions.push(
// 添加contact()函數(shù)
{
label: item, // 顯示名稱
kind: monaco.languages.CompletionItemKind.Function, // 這里Function也可以是別的值,主要用來顯示不同的圖標(biāo)
insertText: item, // 實際粘貼上的值
}
);
});
return {
suggestions, // 必須使用深拷貝
};
},
quickSuggestions: false, // 默認(rèn)提示關(guān)閉
// triggerCharacters: ['$', '.', '='], // 觸發(fā)提示的字符胃碾,可以寫多個
});
editor.focus();
};
return (
<React.Fragment>
<MonacoEditor
width="100%"
height="400px"
language="plaintext"
value={cache.current}
options={options}
onChange={onChangeHandle}
editorDidMount={editorDidMountHandle}
/>
<Tooltip title="校驗" placement="top">
<Icon type="" className={styles['data-check-icon']} onClick={handleCheck} />
</Tooltip>
</React.Fragment>
);
}
);
頁面效果:
備注:
( 使用react-monaco-editor )還有很多坑涨享,網(wǎng)上的介紹文檔也不多,代碼全靠摸索仆百,這里我遇到的一個最大的坑就是自定義提示時厕隧,不能匹配小寫字母,目前已規(guī)避這個問題俄周,并且在gitHub上給作者提了issue吁讨,多發(fā)現(xiàn)問題并分享出來,程序世界會越來越美好峦朗。
react-monaco-editor作者地址:https://github.com/jaywcjlove/react-monacoeditor/issues/26