Skip to content

Commit 42d7c38

Browse files
committed
Add document & Add Codemirror type.
1 parent 1974a18 commit 42d7c38

File tree

12 files changed

+526
-35
lines changed

12 files changed

+526
-35
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
</a>
55
</p>
66

7+
<!--dividing-->
8+
79
<p align="center">
810
<a href="https://github.com/uiwjs/react-markdown-editor/issues">
911
<img src="https://img.shields.io/github/issues/uiwjs/react-markdown-editor.svg">
@@ -22,8 +24,6 @@
2224
</a>
2325
</p>
2426

25-
<!--dividing-->
26-
2727
<p align="center">
2828
A markdown editor with preview, implemented with React.js and TypeScript.
2929
</p>

package-lock.json

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,10 @@
7070
},
7171
"dependencies": {
7272
"@babel/runtime": "^7.4.3",
73+
"@types/highlight.js": "^9.12.3",
7374
"classnames": "^2.2.6",
7475
"codemirror": "^5.45.0",
76+
"highlight.js": "^9.15.6",
7577
"react-markdown": "^4.0.6"
7678
}
7779
}

src/common/codemirror.ts

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,44 @@
11

22
export interface IReactCodeMirrorState {
33
codeMirrorOptions: object,
4-
}
4+
}
5+
6+
7+
export interface IEventDict {
8+
[key: string]: string;
9+
}
10+
11+
12+
export interface ISetScrollOptions {
13+
x?: number | null;
14+
y?: number | null;
15+
}
16+
17+
18+
export interface ISetSelectionOptions {
19+
anchor: CodeMirror.Position;
20+
head: CodeMirror.Position;
21+
}
22+
23+
export interface IGetSelectionOptions {
24+
ranges: ISetSelectionOptions[];
25+
origin: string;
26+
update: (ranges: ISetSelectionOptions[]) => void;
27+
}
28+
29+
/* <tshacks>: laundry list of incorrect typings in @types/codemirror */
30+
export interface IDoc extends CodeMirror.Doc {
31+
setCursor: (pos: CodeMirror.Position, ch?: number, options?: {}) => void;
32+
setSelections: (ranges: ISetSelectionOptions[]) => void;
33+
}
34+
35+
export interface IInstance extends CodeMirror.Editor, IDoc {
36+
options: CodeMirror.EditorConfiguration
37+
}
38+
39+
export interface IDefineModeOptions {
40+
fn: () => CodeMirror.Mode<any>;
41+
name: string;
42+
}
43+
44+
export type DomEvent = (editor: IInstance, event: Event) => void;

src/components/CodeMirror/index.tsx

Lines changed: 104 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,69 @@
1-
import CodeMirror, { Doc, DocConstructor, Editor, EditorConfiguration, EditorFromTextArea } from 'codemirror';
1+
import CodeMirror from 'codemirror';
22
import 'codemirror/mode/markdown/markdown';
33
import React, { Component } from 'react';
4-
import { IReactCodeMirrorState } from '../../common/codemirror';
5-
import { HTMLDivProps, IProps } from '../../common/props';
4+
import { DomEvent, IDefineModeOptions, IEventDict, IGetSelectionOptions, IInstance, ISetScrollOptions, ISetSelectionOptions } from '../../common/codemirror';
5+
import { IProps } from '../../common/props';
66
import './codemirror.less';
77
import './index.less';
88

9-
export interface IReactCodeMirrorProps extends IProps, HTMLDivProps {
9+
declare let global: any;
10+
declare let require: any;
11+
12+
const SERVER_RENDERED = (typeof navigator === 'undefined' || global.PREVENT_CODEMIRROR_RENDER === true);
13+
14+
let cm: any;
15+
if (!SERVER_RENDERED) {
16+
// tslint:disable-next-line: no-var-requires
17+
cm = require('codemirror');
18+
}
19+
20+
export interface ICodeMirror extends IProps {
1021
value?: string,
11-
options: EditorConfiguration,
1222
width?: number | string,
1323
height?: number | string,
24+
className?: string;
25+
cursor?: CodeMirror.Position;
26+
defineMode?: IDefineModeOptions;
27+
editorDidConfigure?: (editor: IInstance) => void;
28+
editorDidMount?: (editor: IInstance, value: string, cb: () => void) => void;
29+
editorWillMount?: () => void;
30+
editorWillUnmount?: (lib: any) => void;
31+
onClear?: (from: CodeMirror.Position, to: CodeMirror.Position) => void;
32+
onBlur?: DomEvent;
33+
onChange?: (editor: IInstance, data: CodeMirror.EditorChange, value: string) => void;
34+
onContextMenu?: DomEvent;
35+
onCopy?: DomEvent;
36+
onCursor?: (editor: IInstance, data: CodeMirror.Position) => void;
37+
onCut?: DomEvent;
38+
onCursorActivity?: (editor: IInstance) => void;
39+
onDblClick?: DomEvent;
40+
onDragEnter?: DomEvent;
41+
onDragLeave?: DomEvent;
42+
onDragOver?: DomEvent
43+
onDragStart?: DomEvent;
44+
onDrop?: DomEvent;
45+
onFocus?: DomEvent
46+
onGutterClick?: (editor: IInstance, lineNumber: number, gutter: string, event: Event) => void;
47+
onKeyDown?: DomEvent;
48+
onKeyPress?: DomEvent;
49+
onKeyUp?: DomEvent;
50+
onMouseDown?: DomEvent;
51+
onPaste?: DomEvent;
52+
onRenderLine?: (editor: IInstance, line: CodeMirror.LineHandle, element: HTMLElement) => void;
53+
onScroll?: (editor: IInstance, data: CodeMirror.ScrollInfo) => void;
54+
onSelection?: (editor: IInstance, data: IGetSelectionOptions) => void;
55+
onTouchStart?: DomEvent;
56+
onUpdate?: (editor: IInstance) => void;
57+
onViewportChange?: (editor: IInstance, start: number, end: number) => void;
58+
options?: CodeMirror.EditorConfiguration
59+
selection?: { ranges: ISetSelectionOptions[], focus?: boolean };
60+
scroll?: ISetScrollOptions;
61+
[key: string]: any,
1462
}
1563

16-
export default class ReactCodeMirror extends Component<IReactCodeMirrorProps, IReactCodeMirrorState> {
17-
public static defaultProps: IReactCodeMirrorProps = {
64+
65+
export default class ReactCodeMirror extends Component<ICodeMirror> {
66+
public static defaultProps: ICodeMirror = {
1867
height: '100%',
1968
options: {
2069
lineNumbers: true,
@@ -25,49 +74,79 @@ export default class ReactCodeMirror extends Component<IReactCodeMirrorProps, IR
2574
width: '100%',
2675
}
2776
public textarea!: HTMLTextAreaElement;
28-
// public editor!: CodeMirror.Editor<Doc>;
29-
// public editor!: Doc | Editor | EditorFromTextArea;
3077
public editor!: any;
31-
// public editor!: CodeMirror.Editor | EditorFromTextArea;
32-
// public editor!: Doc | EditorFromTextArea;
33-
// public editor!: Doc | EditorFromTextArea;
3478
// public editor!: Doc | Editor | EditorFromTextArea | Editor;
35-
public constructor(props: Readonly<IReactCodeMirrorProps>) {
79+
public constructor(props: Readonly<ICodeMirror>) {
3680
super(props);
3781
}
3882
public render() {
3983
return (
40-
<textarea ref={(instance: HTMLTextAreaElement) => { this.textarea = instance; }} />
41-
)
84+
<textarea ref={(instance: HTMLTextAreaElement) => this.textarea = instance} />
85+
);
86+
}
87+
88+
public componentWillMount() {
89+
if (SERVER_RENDERED) {
90+
return;
91+
}
92+
93+
if (this.props.editorWillMount) {
94+
this.props.editorWillMount();
95+
}
4296
}
4397

4498
public componentDidMount() {
99+
if (SERVER_RENDERED) {
100+
return;
101+
}
45102
this.renderCodeMirror(this.props);
46103
}
47104

48-
public async componentWillReceiveProps(nextPros: IReactCodeMirrorProps) {
105+
public async componentWillReceiveProps(nextPros: ICodeMirror) {
49106
this.renderCodeMirror(nextPros);
50107
}
51108

52-
private async renderCodeMirror(props: IReactCodeMirrorProps) {
109+
// 将props中所有的事件处理函数映射并保存
110+
public getEventHandleFromProps(): IEventDict {
111+
const propNames = Object.keys(this.props);
112+
const eventHandle = propNames.filter((prop) => {
113+
return /^on+/.test(prop);
114+
});
115+
116+
const eventDict: IEventDict = {};
117+
eventHandle.forEach((ele) => {
118+
eventDict[ele] = ele.replace(/^on[A-Z]/g, s => s.slice(2).toLowerCase()) as string;
119+
});
120+
121+
return eventDict;
122+
}
123+
124+
private renderCodeMirror(props: ICodeMirror) {
53125
const { value, width, height, options } = props;
126+
127+
if (this.props.defineMode) {
128+
if (this.props.defineMode.name && this.props.defineMode.fn) {
129+
cm.defineMode(this.props.defineMode.name, this.props.defineMode.fn);
130+
}
131+
}
132+
54133
const editorOption = { tabSize: 2, lineNumbers: true, ...options, mode: 'markdown' }
55134
// 生成codemirror实例
56-
this.editor = CodeMirror.fromTextArea(this.textarea, editorOption);
135+
this.editor = cm.fromTextArea(this.textarea, editorOption) as CodeMirror.EditorFromTextArea;
57136
// 获取CodeMirror用于获取其中的一些常量
58-
// this.codemirror = CodeMirror;
59137
// 事件处理映射
60-
// const eventDict = this.getEventHandleFromProps();
61-
// Object.keys(eventDict).forEach((event: string) => {
62-
// this.editor.on(eventDict[event], this.props[event]);
63-
// });
138+
const eventDict = this.getEventHandleFromProps();
139+
Object.keys(eventDict).forEach((event: string) => {
140+
const handle = this.props[event];
141+
this.editor.on(eventDict[event], handle);
142+
});
64143

65-
// 初始化值
144+
// Init value
66145
this.editor.setValue(value || '');
67146
this.editor.setOption(name, editorOption.mode);
68147

69148
if (width || height) {
70-
// 设置尺寸
149+
// Setting size
71150
this.editor.setSize(width, height);
72151
}
73152
}

src/index.tsx

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import classnames from 'classnames';
22
import * as React from "react";
3-
import { HTMLDivProps, IProps } from './common/props';
4-
import CodeMirror from './components/CodeMirror';
3+
import { IProps } from './common/props';
4+
import CodeMirror, { ICodeMirror } from './components/CodeMirror';
55
import ToolBar from './components/ToolBar';
66
import './index.less';
77

8-
export interface IMarkdownEditorProps extends IProps, HTMLDivProps {
9-
prefixCls: string,
8+
export interface IMarkdownEditorProps extends IProps, ICodeMirror {
9+
prefixCls?: string,
1010
value?: string,
1111
height?: number,
1212
toolbars?: string[],
@@ -28,11 +28,15 @@ export default class MarkdownEditor extends React.PureComponent<IMarkdownEditorP
2828
// };
2929
public CodeMirror!: CodeMirror;
3030
public render() {
31-
const { prefixCls, className, toolbars, ...codemirrorProps } = this.props;
31+
const { prefixCls, className, toolbars, onChange, ...codemirrorProps } = this.props;
3232
return (
3333
<div className={classnames(prefixCls, className)}>
3434
<ToolBar toolbars={toolbars} onClick={this.onClick} />
35-
<CodeMirror ref={this.getInstance} {...codemirrorProps} />
35+
<CodeMirror
36+
ref={this.getInstance}
37+
{...codemirrorProps}
38+
onChange={this.onChange}
39+
/>
3640
</div>
3741
);
3842
}
@@ -41,6 +45,10 @@ export default class MarkdownEditor extends React.PureComponent<IMarkdownEditorP
4145
this.CodeMirror = editor;
4246
}
4347
}
48+
private onChange = () => {
49+
// private onChange = (editor: CodeMirror, editorChange: CodeMirror.EditorChange) => {
50+
// console.log('test', editor, editorChange);
51+
}
4452
private onClick = (type: string) => {
4553
const selection = this.CodeMirror.editor.getSelection();
4654
const pos = this.CodeMirror.editor.getCursor();
@@ -56,6 +64,7 @@ export default class MarkdownEditor extends React.PureComponent<IMarkdownEditorP
5664
}
5765
if (type === 'header') {
5866
value = selection ? `# ${selection}` : '# ';
67+
pos.ch = selection ? pos.ch : pos.ch + 2;
5968
}
6069
if (type === 'strike') {
6170
value = selection ? `~~${selection}~~` : '~~~~';
@@ -67,21 +76,25 @@ export default class MarkdownEditor extends React.PureComponent<IMarkdownEditorP
6776
}
6877
if (type === 'olist') {
6978
value = selection ? `- ${selection}` : '- ';
79+
pos.ch = selection ? pos.ch : pos.ch + 2;
7080
}
7181
if (type === 'ulist') {
7282
value = selection ? `1. ${selection}` : '1. ';
83+
pos.ch = selection ? pos.ch : pos.ch + 3;
7384
}
7485
if (type === 'todo') {
7586
value = selection ? `- [ ] ${selection}` : '- [ ] ';
87+
pos.ch = selection ? pos.ch : pos.ch + 6;
7688
}
7789
if (type === 'link') {
78-
value = selection ? `[${selection}](连接地址 "${selection}")` : '[连接说明](连接地址 "连接标题")';
90+
value = '[连接说明](连接地址 "连接标题")';
7991
}
8092
if (type === 'image') {
8193
value = selection ? `${selection} ![](图片地址 "图片描述")` : '![图片描述](图片地址 "图片描述")\n';
8294
}
8395
if (type === 'quote') {
8496
value = selection ? `> ${selection}` : '> ';
97+
pos.ch = selection ? pos.ch : pos.ch + 2;
8598
}
8699
this.CodeMirror.editor.replaceSelection(value);
87100
this.CodeMirror.editor.focus();

website/App.less

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,14 @@
44

55
.editor {
66
width: 980px;
7+
padding: 0 10px;
78
margin: 0 auto 0 auto;
89
}
910

11+
.doc {
12+
padding: 20px 0 0 0;
13+
}
14+
1015
.logo {
1116
svg {
1217
width: auto;

website/App.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import MarkdownEditor from '../src';
44
import styles from './App.less';
55
import Footer from './components/Footer';
66
import GithubCorner from './components/GithubCorner';
7+
import ReactMarkdown from './components/Markdown';
78
import Logo from './Logo';
89

910
export default class App extends React.Component {
@@ -25,6 +26,7 @@ export default class App extends React.Component {
2526
value={DocumentStrSource}
2627
/>
2728
</div>
29+
<ReactMarkdown source={DocumentStrSource} className={styles.doc} />
2830
<Footer name="Kenny Wong" href="" year={2019}/>
2931
</div>
3032
);

0 commit comments

Comments
 (0)