@tntd/monaco-editor 0.0.19 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tntd/monaco-editor",
3
- "version": "0.0.19",
3
+ "version": "1.0.2",
4
4
  "scripts": {
5
5
  "start": "webpack-dev-server --config webpack.config.js",
6
6
  "build": "rm -rf ./dist && webpack --config webpack.config.build.js",
@@ -65,6 +65,7 @@
65
65
  },
66
66
  "main": "dist/index.js",
67
67
  "files": [
68
+ "src",
68
69
  "/dist",
69
70
  "LICENSE",
70
71
  "README.md",
@@ -0,0 +1,206 @@
1
+ import { useEffect, useRef, useMemo, useImperativeHandle, forwardRef, useState } from 'react';
2
+ import { noop, processSize } from './utils';
3
+ import { BaseEditor } from './core/BaseEditor';
4
+ import { SuggestionPlugin } from './plugins/suggestion';
5
+
6
+ // 默认支持的语言
7
+ const defaultLanguages = ['javascript', 'css', 'html', 'json', 'typescript', 'scss', 'less'];
8
+
9
+ const BaseMonacoEditor = forwardRef((props, ref) => {
10
+ const {
11
+ isDiff,
12
+ original,
13
+ modified,
14
+ theme = 'tntd-vs-dark',
15
+ themeColor,
16
+ language,
17
+ defaultValue,
18
+
19
+ width = '100%',
20
+ height = '100%',
21
+ className,
22
+ options,
23
+ readOnly,
24
+
25
+ onChange = noop,
26
+ editorDidMount = noop,
27
+ editorWillMount = noop,
28
+ editorWillUnmount = noop,
29
+ methodList,
30
+ quickList,
31
+ keywords,
32
+
33
+ cb
34
+ } = props;
35
+
36
+ const { triggerCharacters, registerSuggestions } = options || {};
37
+
38
+ // Refs
39
+ const containerRef = useRef();
40
+ const editorRef = useRef();
41
+ const [isReady, setIsReady] = useState(false);
42
+ const isSilentUpdateRef = useRef(false);
43
+
44
+ // 处理宽高
45
+ const style = useMemo(
46
+ () => ({
47
+ width: processSize(width),
48
+ height: processSize(height)
49
+ }),
50
+ [width, height]
51
+ );
52
+
53
+ // 通过 ref 暴露编辑器实例和方法
54
+ useImperativeHandle(
55
+ ref,
56
+ () => {
57
+ // 只有在编辑器实例存在时才暴露方法
58
+ if (!editorRef.current || !isReady) {
59
+ return {};
60
+ }
61
+ cb?.();
62
+ return {
63
+ // 基础方法
64
+ getMonaco: () => editorRef.current?.monaco,
65
+ getEditor: () => editorRef.current?.editor,
66
+ getValue: () => editorRef.current?.getValue(),
67
+ setValue: (value) => editorRef.current?.setValue(value),
68
+ updateOptions: (options) => editorRef.current?.updateOptions(options),
69
+ getPosition: () => editorRef.current?.getPosition(),
70
+ setPosition: (position) => editorRef.current?.setPosition(position),
71
+
72
+ // Diff 相关方法
73
+ getOriginalEditor: () => (isDiff ? editorRef.current?.editor?.getOriginalEditor() : null),
74
+ getModifiedEditor: () => (isDiff ? editorRef.current?.editor?.getModifiedEditor() : null),
75
+
76
+ // 直接暴露实例
77
+ instance: editorRef.current,
78
+ editor: isDiff ? editorRef.current?.editor : editorRef.current?.editor,
79
+ monaco: editorRef.current?.monaco
80
+ };
81
+ },
82
+ [editorRef.current, isReady] // 依赖项增加 isReady
83
+ );
84
+
85
+ // 初始化编辑器
86
+ useEffect(() => {
87
+ const initEditor = async () => {
88
+ try {
89
+ const baseEditor = new BaseEditor(containerRef.current, {
90
+ ...(options || {}),
91
+ readOnly,
92
+ editorWillMount,
93
+ editorWillUnmount,
94
+ className,
95
+ language,
96
+ theme,
97
+ themeColor,
98
+ value: defaultValue,
99
+ original,
100
+ modified,
101
+ isDiff,
102
+ keywords
103
+ });
104
+
105
+ const { monaco, editor } = await baseEditor.init();
106
+ editorRef.current = baseEditor;
107
+
108
+ // 默认的编辑器 都具备 自动提示插件
109
+ baseEditor.use(
110
+ new SuggestionPlugin({
111
+ defaultLanguages,
112
+ language,
113
+ triggerCharacters,
114
+ registerSuggestions,
115
+ methodList,
116
+ quickList
117
+ })
118
+ );
119
+
120
+ // 设置事件监听
121
+ baseEditor?.on?.('change', (value) => {
122
+ if (isSilentUpdateRef.current) {
123
+ return;
124
+ }
125
+ onChange?.(value, { value, editor, monaco });
126
+ });
127
+
128
+ // 标记编辑器已准备就绪
129
+ setIsReady(true);
130
+
131
+ // 回调 - 确保在所有初始化完成后才调用
132
+ if (editorDidMount) {
133
+ await editorDidMount({ baseEditor, monaco, editor });
134
+ }
135
+ } catch (error) {
136
+ console.error('Editor initialization failed:', error);
137
+ }
138
+ };
139
+
140
+ initEditor();
141
+ return () => {
142
+ editorRef.current?.dispose?.();
143
+ setIsReady(false);
144
+ };
145
+ }, []);
146
+
147
+ // 监听语言变化
148
+ useEffect(() => {
149
+ if (!editorRef.current?.editor) return;
150
+ editorRef.current?.monaco.editor.setModelLanguage(editorRef.current.editor.getModel(), language);
151
+ }, [language]);
152
+
153
+ // 监听语言 字段 函数等变化
154
+ useEffect(() => {
155
+ if (!editorRef.current?.editor) return;
156
+ // 重新注册自动提示插件
157
+ editorRef.current.use(
158
+ new SuggestionPlugin({
159
+ defaultLanguages,
160
+ language,
161
+ triggerCharacters,
162
+ registerSuggestions,
163
+ methodList,
164
+ quickList
165
+ })
166
+ );
167
+ }, [language, triggerCharacters, methodList, quickList]);
168
+
169
+ // 监听主题变化
170
+ useEffect(() => {
171
+ if (!editorRef.current?.editor) return;
172
+ editorRef.current.editor.updateOptions({ theme, readOnly });
173
+ }, [theme, readOnly]);
174
+
175
+ // 监听初始值, 静默更新
176
+ useEffect(() => {
177
+ if (!editorRef.current?.editor) return;
178
+ if (!isDiff) {
179
+ if (defaultValue) {
180
+ isSilentUpdateRef.current = true;
181
+ editorRef.current.setValue(defaultValue);
182
+ }
183
+ } else {
184
+ if (original) {
185
+ isSilentUpdateRef.current = true;
186
+ editorRef.current.getModel().original.setValue(original);
187
+ }
188
+ if (modified) {
189
+ isSilentUpdateRef.current = true;
190
+ editorRef.current.getModel().modified.setValue(modified);
191
+ }
192
+ }
193
+ isSilentUpdateRef.current = false;
194
+ }, [defaultValue, modified, isDiff, original, editorRef.current?.editor]);
195
+
196
+ useEffect(() => {
197
+ if (editorRef.current) {
198
+ editorRef.current?.updateOptions(options);
199
+ }
200
+ }, [props.options, editorRef.current]);
201
+
202
+
203
+ return <div ref={containerRef} className={`tnt-react-monaco-editor-container ${className || ''}`} style={style} />;
204
+ });
205
+
206
+ export default BaseMonacoEditor;
@@ -0,0 +1,72 @@
1
+ import BaseMonacoEditor from './BaseMonacoEditor';
2
+ import FormulaEditor from './FormulaEditor';
3
+ import { forwardRef, useRef, useImperativeHandle, useMemo } from 'react';
4
+
5
+ // 默认差异编辑器配置
6
+ const defaultDiffOptions = {
7
+ lineDecorationsWidth: 10,
8
+ // 是否渲染侧边修改指示器
9
+ renderSideBySide: true,
10
+ // 是否突出显示差异
11
+ highlightDifferences: true,
12
+ // 是否显示差异导航器
13
+ diffNavigator: true,
14
+ // 是否忽略空格差异
15
+ ignoreTrimWhitespace: false,
16
+ // 自动布局
17
+ automaticLayout: true,
18
+ smoothScrolling: true,
19
+ wordWrap: 'off', // 或 "on"
20
+ // enableSplitViewResizing: false,
21
+ minimap: { enabled: false },
22
+ renderOverviewRuler: false,
23
+ useInlineViewWhenSpaceIsLimited: false,
24
+ enableSplitViewResizing: false,
25
+ };
26
+
27
+ const DiffEditor = forwardRef((props, ref) => {
28
+ const editorRef = useRef();
29
+
30
+ // 暴露编辑器实例和方法
31
+ useImperativeHandle(
32
+ ref,
33
+ () => {
34
+ if (!editorRef.current) {
35
+ return {};
36
+ }
37
+
38
+ return {
39
+ // 继承基础编辑器或公式编辑器的方法
40
+ ...editorRef.current,
41
+
42
+ // Diff 特有方法
43
+ getDiffNavigator: () => editorRef.current?.editor?.getDiffNavigator(),
44
+ updateDiffOptions: (options) => editorRef.current?.editor?.updateOptions(options),
45
+
46
+ // 直接暴露实例
47
+ instance: editorRef.current,
48
+ editor: editorRef.current?.editor,
49
+ monaco: editorRef.current?.monaco
50
+ };
51
+ },
52
+ [editorRef.current]
53
+ );
54
+
55
+ const EditorComponent = useMemo(() => {
56
+ return props.isFormula ? FormulaEditor : BaseMonacoEditor;
57
+ }, [props.isFormula]);
58
+
59
+ return (
60
+ <EditorComponent
61
+ ref={editorRef}
62
+ isDiff={true}
63
+ {...props}
64
+ options={{
65
+ ...defaultDiffOptions,
66
+ ...props.options
67
+ }}
68
+ />
69
+ );
70
+ });
71
+
72
+ export default DiffEditor;
@@ -0,0 +1,229 @@
1
+ import { useEffect, useRef, useCallback, forwardRef, useImperativeHandle, useState, useMemo } from 'react';
2
+ import BaseMonacoEditor from './BaseMonacoEditor';
3
+ import { ConverterPlugin } from './plugins/converter';
4
+ import { CascaderPlugin } from './plugins/cascader';
5
+ import { v1 as uuidv1 } from 'uuid';
6
+
7
+ import './index.less';
8
+
9
+ // 默认级联配置
10
+ const defaultCascaderOptions = {
11
+ cascaderStyle: { width: '200px' },
12
+ fieldNames: { label: 'name', value: 'value', children: 'children' },
13
+ notSupportArrayData: false,
14
+ isEndMark: false,
15
+ typeMap: {},
16
+ isCascader: true,
17
+ changeOnSelect: true,
18
+ showSourceName: true
19
+ };
20
+
21
+ const FormulaEditor = forwardRef((props, ref) => {
22
+ const {
23
+ isDiff,
24
+ modified: preModified,
25
+ original: preOriginal,
26
+ defaultValue: preDefaultValue,
27
+
28
+ fieldList,
29
+ methodList,
30
+ normalList,
31
+ keyWords = ['int', 'double', 'string', 'list', 'boolean', 'if', 'else', 'and', 'or', 'return'],
32
+ cascaderOptions: cascaderOptionsOrigin,
33
+ regExpState = '@[^\\+\\*\\/#%\\(\\),;\\!\\<\\>\\-=@]*',
34
+ regExp = '',
35
+ searchCb,
36
+ cnCodeToEnExtraLogic,
37
+ enCodeToCnExtraLogic,
38
+ onChange,
39
+ readOnly,
40
+
41
+ editorEvent,
42
+ themeColor
43
+ } = props;
44
+
45
+ const { options, ...restProps } = props;
46
+
47
+ const cascaderOptions = useMemo(() => {
48
+ return {
49
+ ...defaultCascaderOptions,
50
+ ...(cascaderOptionsOrigin || {})
51
+ };
52
+ }, [cascaderOptionsOrigin]);
53
+
54
+ const editorRef = useRef();
55
+ const [editorReady, setEditorReady] = useState(false);
56
+ const converterPluginRef = useRef();
57
+ const [isReady, setIsReady] = useState(false);
58
+ const [defaultValueObj, setDefaultValueObj] = useState({ defaultValue: undefined, modified: undefined, original: undefined });
59
+ const { defaultValue, modified, original } = defaultValueObj;
60
+
61
+ useEffect(() => {
62
+ if (isReady) {
63
+ editorEvent?.(editorRef.current?.instance);
64
+ }
65
+ }, [isReady]);
66
+
67
+ useImperativeHandle(
68
+ ref,
69
+ () => {
70
+ if (!editorRef.current || !isReady) {
71
+ return {};
72
+ }
73
+
74
+ return {
75
+ // 继承 BaseMonacoEditor 的方法
76
+ ...editorRef.current,
77
+
78
+ // Formula 特有方法
79
+ CnCodeToEn: (value) => converterPluginRef.current?.CnCodeToEn(value),
80
+ EnCodeToCn: (value) => converterPluginRef.current?.EnCodeToCn(value),
81
+
82
+ // 获取插件实例
83
+ getConverterPlugin: () => converterPluginRef.current,
84
+ getCascaderPlugin: () => editorRef.current?.editor?.getContribution('cascader')
85
+ };
86
+ },
87
+ [isReady, editorRef.current]
88
+ );
89
+
90
+ // 初始化插件的方法
91
+ const initPlugins = useCallback(
92
+ (baseEditor) => {
93
+ if (baseEditor.plugins.get('suggestion')) {
94
+ baseEditor.plugins.get('suggestion').updateOptions({
95
+ fieldList: fieldList || [],
96
+ methodList: methodList || [],
97
+ normalList: normalList || [],
98
+ keyWords
99
+ });
100
+ }
101
+
102
+ const converterPlugin = new ConverterPlugin({
103
+ regExpState,
104
+ fieldList: fieldList || [],
105
+ methodList: methodList || [],
106
+ normalList: normalList || [],
107
+ keyWords,
108
+ regExp,
109
+ cnCodeToEnExtraLogic,
110
+ enCodeToCnExtraLogic,
111
+ fieldNames: cascaderOptions.fieldNames
112
+ });
113
+
114
+ converterPluginRef.current = converterPlugin;
115
+
116
+ // 初始化正则和模式字段
117
+ converterPlugin.initRegExps();
118
+ const modeField = converterPlugin.getModeField();
119
+
120
+ // 注册插件
121
+ baseEditor.use(converterPlugin);
122
+
123
+ // 注册级联
124
+ const { field, method, regExpState: regExp } = converterPlugin.getRegExps();
125
+ if (!readOnly) {
126
+ baseEditor.use(
127
+ new CascaderPlugin({
128
+ parentContainer: editorRef.current?.instance?.container,
129
+ editor: editorRef.current?.editor,
130
+ fieldList: fieldList || [],
131
+ methodList: methodList || [],
132
+ normalList: normalList || [],
133
+ fieldRegExp: field,
134
+ methodRegExp: method,
135
+ regExpState: regExp,
136
+ searchCb,
137
+ isDiff,
138
+ ...cascaderOptions,
139
+ triggerSuggest: (methodInfo) => {
140
+ baseEditor?.plugins?.get('suggestion')?.triggerSuggest?.(methodInfo);
141
+ },
142
+ clearSuggest: () => {
143
+ baseEditor?.plugins?.get('suggestion')?.clearSuggest?.();
144
+ }
145
+ })
146
+ );
147
+ }
148
+
149
+ // 更新语言配置
150
+ baseEditor.updateLanguage(modeField);
151
+
152
+ // 标记编辑器已准备就绪
153
+ setIsReady(true);
154
+ },
155
+ [fieldList, methodList, editorRef.current?.instance]
156
+ );
157
+
158
+ // 格式化数据值
159
+ const formatDefaultValue = useCallback(() => {
160
+ if (converterPluginRef.current?.modeField) {
161
+ setDefaultValueObj({
162
+ defaultValue: preDefaultValue ? converterPluginRef.current.EnCodeToCn(preDefaultValue) : preDefaultValue,
163
+ modified: preModified ? converterPluginRef.current.EnCodeToCn(preModified) : preModified,
164
+ original: preOriginal ? converterPluginRef.current.EnCodeToCn(preOriginal) : preOriginal
165
+ });
166
+ }
167
+ }, [preDefaultValue, preModified, preOriginal, converterPluginRef.current]);
168
+
169
+ // 监听数据进行convert的upate
170
+ useEffect(() => {
171
+ if (!editorRef.current?.instance) return;
172
+
173
+ initPlugins(editorRef.current?.instance);
174
+
175
+ if (!converterPluginRef.current) return;
176
+
177
+ converterPluginRef.current.updateData({
178
+ fieldList: fieldList || [],
179
+ methodList: methodList || [],
180
+ normalList: normalList || [],
181
+ regExp,
182
+ cnCodeToEnExtraLogic,
183
+ enCodeToCnExtraLogic
184
+ });
185
+
186
+ const modeField = converterPluginRef.current.getModeField();
187
+
188
+ // 转换默认值
189
+ formatDefaultValue();
190
+
191
+ // 更新语言配置
192
+ editorRef.current?.instance.updateLanguage(modeField);
193
+ }, [editorReady, fieldList, methodList, normalList, regExp, preDefaultValue, preModified, preOriginal]);
194
+
195
+ // 监听数据变化
196
+ const handleChange = (value, obj) => {
197
+ const enCode = editorRef.current?.instance.CnCodeToEn(value);
198
+ onChange?.(enCode, { ...obj, enCode, cnCode: value });
199
+ };
200
+
201
+ const editorReadyCb = () => {
202
+ setEditorReady(true);
203
+ };
204
+
205
+ const uuid = useMemo(() => {
206
+ return uuidv1()?.replace(/-/g, '');
207
+ }, []);
208
+
209
+ return (
210
+ <BaseMonacoEditor
211
+ ref={editorRef}
212
+ {...restProps}
213
+ defaultValue={defaultValue}
214
+ modified={modified}
215
+ original={original}
216
+ language={'formulaLang_' + uuid}
217
+ theme="formulaTheme"
218
+ themeColor={themeColor}
219
+ options={{
220
+ ...options,
221
+ isFormula: true
222
+ }}
223
+ onChange={handleChange}
224
+ cb={editorReadyCb}
225
+ />
226
+ );
227
+ });
228
+
229
+ export default FormulaEditor;
package/src/I18N.js ADDED
@@ -0,0 +1,7 @@
1
+ import Cookies from 'universal-cookie';
2
+ const cookies = new Cookies();
3
+
4
+ export const getLang = () => {
5
+ return cookies.get('lang') || 'cn';
6
+ };
7
+