Skip to content

Commit ec63107

Browse files
committed
feat: add dt-react-codemirror-editor component
1 parent 0485c74 commit ec63107

File tree

13 files changed

+798
-22
lines changed

13 files changed

+798
-22
lines changed

src/codemirror/config.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
export const defaultEditorOptions = {
2+
mode: 'text/x-sql',
3+
lint: true,
4+
indentWithTabs: true,
5+
smartIndent: true,
6+
lineNumbers: true,
7+
autofocus: false,
8+
lineWrapping: true,
9+
readOnly: false
10+
};
11+
12+
export const propEditorOptions = { // 编辑器选项
13+
mode: 'text/x-properties',
14+
lint: true,
15+
indentWithTabs: true,
16+
smartIndent: true,
17+
lineNumbers: true,
18+
autofocus: false
19+
}
20+
21+
export const jsonEditorOptions = { // json编辑器选项
22+
mode: 'application/json',
23+
lint: true,
24+
indentWithTabs: true,
25+
smartIndent: true,
26+
lineNumbers: true,
27+
autofocus: false,
28+
matchBrackets: true
29+
}

src/codemirror/index.tsx

Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
import * as React from 'react'
2+
3+
import { defaultEditorOptions } from './config'
4+
5+
import CodeMirror from 'codemirror';
6+
import './languages/log';
7+
import './languages/simpleLog';
8+
import 'codemirror/lib/codemirror.css'
9+
import 'codemirror/addon/lint/lint.css'
10+
import 'codemirror/addon/scroll/simplescrollbars.css'
11+
import './style.scss'
12+
import { getLinkMark, getLogMark } from './utils'
13+
14+
require('codemirror/mode/sql/sql')
15+
require('codemirror/mode/python/python')
16+
require('codemirror/mode/javascript/javascript')
17+
require('codemirror/mode/properties/properties')
18+
require('codemirror/addon/display/placeholder')
19+
require('codemirror/addon/edit/matchbrackets')
20+
require('codemirror/addon/scroll/simplescrollbars')
21+
22+
type EditorEventCallback = (prevValue: string, nextValue: string, editorDoc: CodeMirror.Doc) => void;
23+
24+
interface CodeMirrorEditorProps {
25+
26+
value: string;
27+
/**
28+
* The cursor position
29+
*/
30+
cursor?: CodeMirror.Position;
31+
/**
32+
* Whether sync the editor value when props value changed
33+
*/
34+
sync?: boolean;
35+
/**
36+
* Editor option
37+
*/
38+
options?: CodeMirror.EditorConfiguration;
39+
/**
40+
* Always show cursor in the end.
41+
* This option be used when streaming output, such as typing logs.
42+
*/
43+
cursorAlwaysInEnd?: boolean;
44+
editorRef?: (inst: CodeMirror.Editor) => void;
45+
cursorActivity?: EditorEventCallback;
46+
onChange?: EditorEventCallback;
47+
onFocus?: EditorEventCallback;
48+
focusOut?: EditorEventCallback;
49+
50+
className?: string;
51+
style?: React.CSSProperties;
52+
53+
}
54+
55+
class CodeMirrorEditor extends React.Component<CodeMirrorEditorProps, any> {
56+
private _editorRef: HTMLTextAreaElement;
57+
private _codeMirrorInstance: CodeMirror.Editor;
58+
59+
componentDidMount () {
60+
const ele = this._editorRef;
61+
if (!ele) return;
62+
63+
const {
64+
value, onChange, onFocus, cursor, options,
65+
focusOut, cursorActivity, editorRef
66+
} = this.props;
67+
68+
const editorConfig = Object.assign({}, defaultEditorOptions, options);
69+
70+
this._codeMirrorInstance = CodeMirror.fromTextArea(ele, editorConfig);
71+
const editorIns = this._codeMirrorInstance;
72+
this.renderTextMark();
73+
74+
// 设置 cursor 位置
75+
if (cursor) editorIns.setCursor(cursor)
76+
77+
editorIns.on('change', (doc: CodeMirror.Doc) => {
78+
if (onChange) {
79+
onChange(value, doc.getValue(), doc)
80+
}
81+
})
82+
editorIns.on('focus', (doc: CodeMirror.Doc) => {
83+
if (onFocus) {
84+
onFocus(value, doc.getValue(), doc)
85+
}
86+
})
87+
88+
editorIns.on('blur', (doc: CodeMirror.Doc) => {
89+
if (focusOut) {
90+
focusOut(value, doc.getValue(), doc)
91+
}
92+
})
93+
94+
editorIns.on('cursorActivity', (doc: CodeMirror.Doc) => {
95+
if (cursorActivity) {
96+
cursorActivity(value, doc.getValue(), doc)
97+
}
98+
})
99+
if (editorRef) {
100+
editorRef(editorIns);
101+
}
102+
}
103+
104+
static fromTextArea(ele: HTMLTextAreaElement, options: CodeMirror.EditorConfiguration): any {
105+
throw new Error("Method not implemented.");
106+
}
107+
108+
// eslint-disable-next-line
109+
UNSAFE_componentWillReceiveProps(nextProps: any) {
110+
const editorIns = this._codeMirrorInstance;
111+
const { value, sync, cursor, cursorAlwaysInEnd, options = {} } = nextProps;
112+
if (options) {
113+
editorIns.setOption('readOnly', options.readOnly)
114+
}
115+
116+
if (this.props.value !== value) {
117+
if (cursor) editorIns.setCursor(cursor)
118+
if (sync) {
119+
120+
const scrollInfo = editorIns.getScrollInfo();
121+
/**
122+
* 判断滚动条是不是在底部
123+
*/
124+
const isInBottom = (scrollInfo.top + scrollInfo.clientHeight) - scrollInfo.height > -10;
125+
console.log(isInBottom);
126+
if (!value) {
127+
editorIns.setValue('')
128+
} else {
129+
editorIns.setValue(value);
130+
}
131+
if (cursorAlwaysInEnd) {
132+
editorIns.setCursor(editorIns.lineCount());
133+
} else if (!isInBottom) {
134+
/**
135+
* 不在底部并且不设置自动滚到底部,则滚到原来位置
136+
*/
137+
editorIns.scrollTo(scrollInfo.left, scrollInfo.top)
138+
} else if (isInBottom) {
139+
/**
140+
* 在底部,则自动到底部
141+
* 需要等setValue这个动作结束之后,再获取内容的高度。
142+
*/
143+
Promise.resolve().then(() => {
144+
let nowScrollInfo = editorIns.getScrollInfo();
145+
editorIns.scrollTo(nowScrollInfo.left, nowScrollInfo.height)
146+
})
147+
}
148+
}
149+
this.renderTextMark();
150+
}
151+
}
152+
renderTextMark () {
153+
const editorIns = this._codeMirrorInstance;
154+
155+
const marks = editorIns.getAllMarks();
156+
for (let mark of marks) { // 重置marks
157+
mark.clear();
158+
}
159+
const value = editorIns.getValue();
160+
const linkMarks: any = [].concat(getLinkMark(value)).concat(getLogMark(value));
161+
for (let _i = 0; _i < linkMarks.length; _i++) {
162+
let mark = linkMarks[_i];
163+
editorIns.markText(
164+
editorIns.posFromIndex(mark.start),
165+
editorIns.posFromIndex(mark.end),
166+
{ replacedWith: mark.node }
167+
)
168+
}
169+
}
170+
171+
render () {
172+
const { className, style } = this.props
173+
let renderClass = `code-editor ${className || ''}`;
174+
let renderStyle: React.CSSProperties = {
175+
position: 'relative'
176+
};
177+
178+
Object.assign(renderStyle, style);
179+
180+
return (
181+
<div
182+
className={renderClass}
183+
style={renderStyle}>
184+
<textarea
185+
ref={(e: HTMLTextAreaElement) => { this._editorRef = e }}
186+
defaultValue={this.props.value || ''}
187+
/>
188+
</div>
189+
)
190+
}
191+
}
192+
193+
export default CodeMirrorEditor
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import 'codemirror/addon/mode/simple';
2+
const codemirror = require('codemirror')
3+
4+
const startRegex: any = [
5+
{
6+
regex: /(\[.*?\])([ \t]*)(<error>)/,
7+
token: ['tag', null, 'error.strong'],
8+
sol: true,
9+
next: 'error'
10+
},
11+
{
12+
regex: /(\[.*?\])([ \t]*)(<info>)/,
13+
token: ['tag', null, 'bracket'],
14+
sol: true,
15+
next: 'info'
16+
},
17+
{
18+
regex: /(\[.*?\])([ \t]*)(<warning>)/,
19+
token: ['tag', null, 'comment'],
20+
sol: true,
21+
next: 'warning'
22+
}
23+
]
24+
25+
codemirror.defineSimpleMode('dtlog', {
26+
start: [
27+
...startRegex,
28+
{
29+
regex: /.*/,
30+
token: 'hr'
31+
}
32+
],
33+
error: [
34+
...startRegex,
35+
{
36+
regex: /.*/,
37+
token: 'error.strong'
38+
}
39+
],
40+
info: [
41+
...startRegex,
42+
{
43+
regex: /.*/,
44+
token: 'bracket'
45+
}
46+
],
47+
warning: [
48+
...startRegex,
49+
{
50+
regex: /.*/,
51+
token: 'comment'
52+
}
53+
]
54+
});
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import 'codemirror/addon/mode/simple';
2+
const codemirror = require('codemirror')
3+
4+
codemirror.defineSimpleMode('simpleLog', {
5+
start: [
6+
{
7+
regex: /^[=]+[^=]*[=]+/,
8+
token: 'strong'
9+
},
10+
{
11+
regex: /([^\w])([A-Z][\w]*)/,
12+
token: [null, 'string']
13+
},
14+
{
15+
regex: /(^[A-Z][\w]*)/,
16+
token: 'string'
17+
}
18+
// {
19+
// regex: /([^\d])([0-9]+)/,
20+
// token: [null, 'comment']
21+
// },
22+
// {
23+
// regex: /(^[0-9]+)/,
24+
// token: 'comment'
25+
// }
26+
]
27+
});

src/codemirror/style.scss

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
.CodeMirror, .CodeMirror-merge-pane{
2+
height:inherit !important;
3+
font-family: consolas !important;
4+
}
5+
.editor_custom_link{
6+
cursor: pointer;
7+
color:#1474f1;
8+
text-decoration:underline;
9+
}
10+
.editor_custom_link:hover{
11+
color:#04b4fa;
12+
}
13+
.c-editor--log__error{
14+
color:#bb0606;
15+
font-weight: bold;
16+
}
17+
.c-editor--log__info{
18+
color:#333333;
19+
font-weight: bold;
20+
}
21+
.c-editor--log__warning{
22+
color:#ee9900;
23+
}
24+
.c-editor--log__success{
25+
color:#669600;
26+
}

0 commit comments

Comments
 (0)