Skip to content

Commit 1c6985d

Browse files
committed
Add preview markdown.
1 parent 296a20d commit 1c6985d

File tree

7 files changed

+296
-25
lines changed

7 files changed

+296
-25
lines changed

src/components/CodeMirror/index.tsx

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,18 @@ export default class ReactCodeMirror extends Component<ICodeMirror> {
9999
if (SERVER_RENDERED) {
100100
return;
101101
}
102+
103+
const { options } = this.props;
104+
if (this.props.defineMode) {
105+
if (this.props.defineMode.name && this.props.defineMode.fn) {
106+
cm.defineMode(this.props.defineMode.name, this.props.defineMode.fn);
107+
}
108+
}
109+
110+
const editorOption = { tabSize: 2, lineNumbers: true, ...options, mode: 'markdown' };
111+
// 生成codemirror实例
112+
this.editor = cm.fromTextArea(this.textarea, editorOption) as CodeMirror.EditorFromTextArea;
113+
102114
this.renderCodeMirror(this.props);
103115
}
104116

@@ -122,28 +134,17 @@ export default class ReactCodeMirror extends Component<ICodeMirror> {
122134
}
123135

124136
private renderCodeMirror(props: ICodeMirror) {
125-
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-
133-
const editorOption = { tabSize: 2, lineNumbers: true, ...options, mode: 'markdown' }
134-
// 生成codemirror实例
135-
this.editor = cm.fromTextArea(this.textarea, editorOption) as CodeMirror.EditorFromTextArea;
137+
const { value, width, height } = props;
136138
// 获取CodeMirror用于获取其中的一些常量
137139
// 事件处理映射
138140
const eventDict = this.getEventHandleFromProps();
139141
Object.keys(eventDict).forEach((event: string) => {
140142
const handle = this.props[event];
141143
this.editor.on(eventDict[event], handle);
142144
});
143-
144145
// Init value
145146
this.editor.setValue(value || '');
146-
this.editor.setOption(name, editorOption.mode);
147+
// this.editor.setOption(name, editorOption.mode);
147148

148149
if (width || height) {
149150
// Setting size
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
code {
2+
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace;
3+
word-wrap: normal;
4+
}
5+
6+
pre code {
7+
padding: 16px;
8+
font-size: 95%;
9+
line-height: 1.5;
10+
display: block;
11+
text-shadow: 0 1px #fff;
12+
}
13+
pre {
14+
margin-bottom: 18px;
15+
max-height: 35em;
16+
position: relative;
17+
overflow: auto;
18+
background-color: #F0F0F0;
19+
border-radius: 3px;
20+
}
21+
del {
22+
color: #888;
23+
}
24+
pre code {
25+
background: none;
26+
font-size: 1em;
27+
overflow-wrap: normal;
28+
white-space: inherit;
29+
}
30+
ul, li, ol, dl, dt {
31+
list-style: inherit;
32+
}
33+
ul, ol {
34+
padding-left: 2em;
35+
}
36+
dl {
37+
padding: 0;
38+
dt {
39+
padding: 0;
40+
margin-top: 16px;
41+
font-size: 14px;
42+
font-style: italic;
43+
font-weight: 600;
44+
}
45+
}
46+
li + li {
47+
margin-top: 3px;
48+
}
49+
a {
50+
color: #0366d6;
51+
text-decoration: none;
52+
}
53+
p, ol, ul {
54+
margin-bottom: 16px;
55+
}
56+
li {
57+
display: list-item;
58+
}
59+
60+
h1 {
61+
padding-bottom: 0.3em;
62+
font-size: 2em;
63+
border-bottom: 1px solid #cecece;
64+
margin-bottom: 10px;
65+
}
66+
67+
h2 {
68+
margin-bottom: 10px;
69+
padding-bottom: 0.3em;
70+
font-size: 1.5em;
71+
border-bottom: 1px solid #cecece;
72+
}
73+
74+
h3 {
75+
margin-bottom: 10px;
76+
font-size: 1.25em
77+
}
78+
79+
h4 {
80+
margin-bottom: 8px;
81+
font-size: 1em
82+
}
83+
84+
h5 {
85+
margin-bottom: 8px;
86+
font-size: 0.875em
87+
}
88+
89+
h6 {
90+
margin-bottom: 5px;
91+
font-size: 0.85em;
92+
color: #6a737d
93+
}
94+
95+
blockquote {
96+
margin: 0;
97+
padding: 0 1em;
98+
margin-bottom: 10px;
99+
color: #6a737d;
100+
border-left: 0.25em solid #dfe2e5;
101+
& >:first-child {
102+
margin-top: 0;
103+
}
104+
& >:last-child {
105+
margin-bottom: 0;
106+
}
107+
}
108+
109+
hr {
110+
height: 0.25em;
111+
padding: 0;
112+
margin: 24px 0;
113+
background-color: #e1e4e8;
114+
border: 0;
115+
}
116+
117+
118+
table {
119+
margin-bottom: 16px;
120+
border-collapse: collapse;
121+
border-spacing: 0;
122+
width: 100%;
123+
display: block;
124+
width: 100%;
125+
}
126+
127+
table th {
128+
font-weight: 600;
129+
}
130+
131+
table th,table td {
132+
padding: 6px 13px;
133+
border: 1px solid #dfe2e5;
134+
}
135+
136+
table tr {
137+
background-color: #fff;
138+
border-top: 1px solid #c6cbd1;
139+
}
140+
141+
table tr:nth-child(2n) {
142+
background-color: #f6f8fa;
143+
}
144+
145+
table img {
146+
background-color: transparent;
147+
}
148+
149+
img {
150+
max-width: 100%;
151+
box-sizing: content-box;
152+
vertical-align: middle;
153+
}
154+
155+
img[align=right] {
156+
padding-left: 20px;
157+
}
158+
159+
img[align=left] {
160+
padding-right: 20px;
161+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
@editor-prefix:~"md-editor";
2+
3+
:global {
4+
.@{editor-prefix} {
5+
&-preview {
6+
width: 0%;
7+
overflow: hidden;
8+
border-left: 0;
9+
position: absolute;
10+
right: 0;
11+
top: 0;
12+
bottom: 0;
13+
}
14+
&-visble {
15+
border-left: 1px solid #dfdfe0;
16+
overflow: auto;
17+
width: 50%;
18+
}
19+
&-markdown {
20+
padding: 20px;
21+
@import "./default.less";
22+
}
23+
}
24+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import classnames from 'classnames'
2+
import React from 'react';
3+
import ReactMarkdown, { MarkdownAbstractSyntaxTree } from 'react-markdown';
4+
import { IProps } from '../../common/props';
5+
import './index.less';
6+
7+
export interface IPreviewMarkdown extends IProps {
8+
visble: boolean;
9+
value?: string;
10+
prefixCls?: string;
11+
style?: React.CSSProperties;
12+
}
13+
14+
export interface IPreviewMarkdownState {
15+
value?: string;
16+
}
17+
18+
export default class PreviewMarkdown extends React.Component<IPreviewMarkdown, IPreviewMarkdownState> {
19+
public static defaultProps: IPreviewMarkdown = {
20+
prefixCls: 'md-editor',
21+
visble: true,
22+
}
23+
constructor(props: IPreviewMarkdown) {
24+
super(props);
25+
this.state = {
26+
value: props.value,
27+
}
28+
}
29+
30+
public updateSource(value: string) {
31+
this.setState({ value });
32+
}
33+
34+
public render() {
35+
const { prefixCls, visble, value, ...elementProps } = this.props;
36+
return (
37+
<div
38+
className={classnames(`${prefixCls}-preview`, {
39+
[`${prefixCls}-visble`]: visble
40+
})}
41+
{...elementProps}
42+
>
43+
<ReactMarkdown
44+
className={classnames(`${prefixCls}-markdown`)}
45+
source={this.state.value}
46+
escapeHtml={false}
47+
allowNode={this.allowNode}
48+
/>
49+
</div>
50+
);
51+
}
52+
private allowNode = (node: MarkdownAbstractSyntaxTree) => {
53+
if (node.type === 'html' && node.value) {
54+
if (/<!--([^]+?)-->/.test(node.value)) {
55+
return false;
56+
}
57+
}
58+
return true;
59+
}
60+
}

src/index.less

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,8 @@
55
box-shadow: 0 0 0 1px rgba(16, 22, 26, 0.1), 0 0 0 rgba(16, 22, 26, 0), 0 1px 1px rgba(16, 22, 26, 0.2);
66
border-radius: 3px;
77
position: relative;
8+
&-content {
9+
position: relative;
10+
}
811
}
912
}

src/index.tsx

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import * as React from "react";
33
import { IInstance } from './common/codemirror';
44
import { IProps } from './common/props';
55
import CodeMirror, { ICodeMirror } from './components/CodeMirror';
6+
import PreviewMarkdown from './components/PreviewMarkdown';
67
import ToolBar from './components/ToolBar';
78
import './index.less';
89

@@ -15,7 +16,7 @@ export interface IMarkdownEditor extends IProps, ICodeMirror {
1516
}
1617

1718
interface IMarkdownEditorState {
18-
editor?: CodeMirror;
19+
preview: boolean;
1920
}
2021

2122
export default class MarkdownEditor extends React.PureComponent<IMarkdownEditor, IMarkdownEditorState, {}> {
@@ -25,20 +26,33 @@ export default class MarkdownEditor extends React.PureComponent<IMarkdownEditor,
2526
prefixCls: 'md-editor',
2627
value: '',
2728
};
28-
// public state: IMarkdownEditorState = {
29-
// editor: undefined,
30-
// };
29+
public preview!: PreviewMarkdown;
3130
public CodeMirror!: CodeMirror;
31+
constructor(props: IMarkdownEditor) {
32+
super(props);
33+
this.state = {
34+
preview: true,
35+
}
36+
}
3237
public render() {
3338
const { prefixCls, className, toolbars, onChange, ...codemirrorProps } = this.props;
3439
return (
3540
<div className={classnames(prefixCls, className)}>
3641
<ToolBar toolbars={toolbars} onClick={this.onClick} />
37-
<CodeMirror
38-
ref={this.getInstance}
39-
{...codemirrorProps}
40-
onChange={this.onChange}
41-
/>
42+
<div className={classnames(`${prefixCls}-content`)}>
43+
<CodeMirror
44+
style={{ width: this.state.preview ? '50%' : '100%' }}
45+
width={this.state.preview ? '50%' : '100%'}
46+
ref={this.getInstance}
47+
{...codemirrorProps}
48+
onChange={this.onChange}
49+
/>
50+
<PreviewMarkdown
51+
ref={(pmd: PreviewMarkdown) => this.preview = pmd}
52+
value={this.props.value}
53+
visble={this.state.preview}
54+
/>
55+
</div>
4256
</div>
4357
);
4458
}
@@ -50,10 +64,20 @@ export default class MarkdownEditor extends React.PureComponent<IMarkdownEditor,
5064
private onChange = (editor: IInstance, data: CodeMirror.EditorChange, value: string) => {
5165
const { onChange } = this.props as IMarkdownEditor;
5266
if (onChange) {
67+
if (this.preview) {
68+
this.preview.updateSource(editor.getValue());
69+
}
5370
onChange(editor, data, value);
5471
}
5572
}
73+
private previewMarkdown() {
74+
this.setState({ preview: !this.state.preview });
75+
}
5676
private onClick = (type: string) => {
77+
if (type === 'preview') {
78+
this.previewMarkdown();
79+
return;
80+
}
5781
const selection = this.CodeMirror.editor.getSelection();
5882
const pos = this.CodeMirror.editor.getCursor();
5983
let value = '';

0 commit comments

Comments
 (0)