2021/02/28更
此次更新主要增加了给编辑器鼠标焦点处设置值方法
/**
* 给编辑器设置值
* @param value 需要设置的值
*/
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();
}
};
开始
最近产品上用到了代码编辑器,因为技术栈使用的是react, 找了许久经过甄选最后决定使用monaco-editor的react封装版本:react-monaco-edotor,下面是他的简单介绍:
经过很多前辈大佬的努力,react-monaco-edotor横空出世,对于使用react技术栈的人是一个再好不过的好消息了,介绍完毕我们讲讲如何使用吧。
使用方法:
yarn add monaco-editor -D
yarn add react-monaco-editor -D
yarn add monaco-editor-webpack-plugin -D
安装好依赖包再去webpack-config中配置相关依赖:
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']),
...
]
注意:自定义提示时一定需设置 language: 'plaintext' 自定义文本, 并且在monaco.languages.registerCompletionItemProvider中使用深拷贝
经过以上步骤 编辑器就能正常应用到项目中去了,使用及自定义提示的使用方法:
/*
* @filename:
* @Date: 2020-05-26 15:11:32
* @Author: 汪渊
* @version: 1.0.0
* @copyright: copyright: HAND ® 2020
*/
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;
secondRightData: (model.dataSource.BaseDataSourceField | model.dataSource.BaseDataSourceHeader)[];
}
export default forwardRef(
({ currentRecord, handleCheck, secondRightData = [] }: IRightContent, ref) => {
// 编辑器实例
const editorInstance = useRef();
// 真实数据
const code: any = useRef(currentRecord ? currentRecord.formulaContent : '');
// 缓存数据
const cache: any = useRef(code.current);
// 输入引号触发页面刷新
const [refresh, setRefresh] = React.useState(false);
const monacoInstance: any = useRef();
const options = {
selectOnLineNumbers: true,
renderSideBySide: false,
};
useImperativeHandle(ref, () => ({
handleSetEditorVal,
getEditorData: () => cache.current,
}));
useEffect(
() => () => {
provider.dispose(); // 弹窗关闭后 销毁编辑器实例
},
[]
);
/**
* 给编辑器设置值
* @param value 需要设置的值
*/
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回调
* @param {String} val 当前编辑器的值
*/
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; // 数组长度永远为1
});
// 提示项设值
provider = monaco.languages.registerCompletionItemProvider('plaintext', {
provideCompletionItems() {
const suggestions: ISuggestions[] = [];
if (code && code.current) {
code.current.forEach((record) => {
suggestions.push({
// label未写错 中间加空格为了隐藏大写字段名称 大写字段名称用于规避自定义提示不匹配小写的bug
label:
record.label ||
`${record.displayName} (${
record.aliasName
}) ${''}(${record.aliasName.toUpperCase()})`, // 显示名称
kind: record.kind || monaco.languages.CompletionItemKind.Field, // 这里Function也可以是别的值,主要用来显示不同的图标
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()函数
{
label: item, // 显示名称
kind: monaco.languages.CompletionItemKind.Function, // 这里Function也可以是别的值,主要用来显示不同的图标
insertText: item, // 实际粘贴上的值
}
);
});
return {
suggestions, // 必须使用深拷贝
};
},
quickSuggestions: false, // 默认提示关闭
// triggerCharacters: ['$', '.', '='], // 触发提示的字符,可以写多个
});
editor.focus();
};
return (
);
}
);
页面效果:
备注:
( 使用react-monaco-editor )还有很多坑,网上的介绍文档也不多,代码全靠摸索,这里我遇到的一个最大的坑就是自定义提示时,不能匹配小写字母,目前已规避这个问题,并且在gitHub上给作者提了issue,多发现问题并分享出来,程序世界会越来越美好。
react-monaco-editor作者地址:https://github.com/jaywcjlove/react-monacoeditor/issues/26