Skip to content

Commit a38508f

Browse files
committed
Add a separation preview markdown.
1 parent e21b31a commit a38508f

File tree

4 files changed

+193
-163
lines changed

4 files changed

+193
-163
lines changed

Diff for: src/MDEditor.tsx

+163
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
import React from 'react';
2+
import classnames from 'classnames';
3+
import { ReactMarkdownProps } from 'react-markdown';
4+
import { IProps, ICommand, CommandOrchestrator } from './Type';
5+
import TextArea, { ITextAreaProps } from './components/TextArea';
6+
import Toolbar from './components/Toolbar';
7+
import DragBar from './components/DragBar';
8+
import MarkdownPreview from './components/Markdown';
9+
import { getCommands, TextAreaCommandOrchestrator } from './commands';
10+
import './index.less';
11+
import './markdowncolor.less';
12+
import './markdown.less';
13+
14+
export interface IMDEditorProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'onChange'>, IProps {
15+
/**
16+
* The Markdown value.
17+
*/
18+
value?: string;
19+
/**
20+
* Event handler for the `onChange` event.
21+
*/
22+
onChange?: (value: string) => void;
23+
/**
24+
* Can be used to make `Markdown Editor` focus itself on initialization. Defaults to on.
25+
* it will be set to true when either the source `textarea` is focused,
26+
* or it has an `autofocus` attribute and no other element is focused.
27+
*/
28+
autoFocus?: ITextAreaProps['autoFocus'];
29+
/**
30+
* The height of the editor.
31+
*/
32+
height?: React.CSSProperties['height'];
33+
/**
34+
* Show drag and drop tool. Set the height of the editor.
35+
*/
36+
visiableDragbar?: boolean;
37+
/**
38+
* Show markdown preview.
39+
*/
40+
preview?: 'live' | 'edit' | 'preview';
41+
fullscreen?: boolean;
42+
/**
43+
* Maximum drag height. `visiableDragbar=true`
44+
*/
45+
maxHeight?: number;
46+
previewOptions?: ReactMarkdownProps;
47+
/**
48+
* Minimum drag height. `visiableDragbar=true`
49+
*/
50+
minHeight?: number;
51+
/**
52+
* You can create your own commands or reuse existing commands.
53+
*/
54+
commands?: ICommand[];
55+
}
56+
57+
export interface IMDEditorState {
58+
height: React.CSSProperties['height'];
59+
preview?: IMDEditorProps['preview'];
60+
fullscreen?: boolean;
61+
}
62+
63+
export default class MDEditor extends React.PureComponent<IMDEditorProps, IMDEditorState> {
64+
static Markdown = MarkdownPreview;
65+
public static displayName = 'MDEditor';
66+
public preview = React.createRef<MarkdownPreview>();
67+
public textarea = React.createRef<TextArea>();
68+
public commandOrchestrator!: CommandOrchestrator;
69+
public static defaultProps: IMDEditorProps = {
70+
value: '',
71+
prefixCls: 'w-md-editor',
72+
height: 200,
73+
minHeight: 100,
74+
maxHeight: 1200,
75+
visiableDragbar: true,
76+
preview: 'live',
77+
fullscreen: false,
78+
commands: getCommands(),
79+
}
80+
public constructor(props: IMDEditorProps) {
81+
super(props);
82+
this.state = {
83+
height: props.height,
84+
preview: props.preview,
85+
fullscreen: props.fullscreen,
86+
};
87+
}
88+
public componentDidMount() {
89+
this.handleChange(this.props.value);
90+
this.commandOrchestrator = new TextAreaCommandOrchestrator(this.textarea.current!.text.current as HTMLTextAreaElement);
91+
}
92+
public UNSAFE_componentWillReceiveProps(nextProps: IMDEditorProps) {
93+
if (nextProps.preview !== this.props.preview) {
94+
this.setState({ preview: nextProps.preview });
95+
}
96+
if (nextProps.fullscreen !== this.props.fullscreen) {
97+
this.setState({ fullscreen: nextProps.fullscreen });
98+
}
99+
}
100+
private handleChange(mdStr?: string) {
101+
const { onChange } = this.props;
102+
this.preview.current!.renderHTML(mdStr);
103+
onChange && onChange(mdStr || '');
104+
}
105+
public handleCommand = (command: ICommand) => {
106+
if (command.keyCommand === 'preview') {
107+
this.setState({ preview: command.value as IMDEditorState['preview'] });
108+
}
109+
if (command.keyCommand === 'fullscreen') {
110+
this.setState({ fullscreen: !this.state.fullscreen });
111+
document.body.style.overflow = this.state.fullscreen ? 'initial' : 'hidden';
112+
}
113+
this.commandOrchestrator.executeCommand(command);
114+
}
115+
public render() {
116+
const { prefixCls, className, value, commands, height, visiableDragbar, preview, fullscreen, previewOptions, maxHeight, minHeight, autoFocus, onChange, ...other } = this.props;
117+
const cls = classnames(className, prefixCls, {
118+
[`${prefixCls}-show-${this.state.preview}`]: this.state.preview,
119+
[`${prefixCls}-fullscreen`]: this.state.fullscreen,
120+
});
121+
return (
122+
<div className={cls} style={{ height: this.state.fullscreen ? '100%' : this.state.height }} {...other}>
123+
<Toolbar
124+
active={{
125+
fullscreen: this.state.fullscreen,
126+
preview: this.state.preview,
127+
}}
128+
prefixCls={prefixCls} commands={commands}
129+
onCommand={this.handleCommand}
130+
/>
131+
<div
132+
className={`${prefixCls}-content`}
133+
style={{ height: this.state.fullscreen ? 'calc(100% - 29px)' : (this.state.height as number) - 29 }}
134+
>
135+
<TextArea
136+
ref={this.textarea}
137+
className={`${prefixCls}-input`}
138+
prefixCls={prefixCls}
139+
value={value}
140+
autoFocus={autoFocus}
141+
onChange={this.handleChange.bind(this)}
142+
/>
143+
<MarkdownPreview
144+
{...previewOptions}
145+
ref={this.preview}
146+
className={`${prefixCls}-preview`}
147+
/>
148+
{visiableDragbar && !this.state.fullscreen && (
149+
<DragBar
150+
prefixCls={prefixCls}
151+
height={this.state.height as number}
152+
maxHeight={maxHeight!}
153+
minHeight={minHeight!}
154+
onChange={(newHeight) => {
155+
this.setState({ height: newHeight });
156+
}}
157+
/>
158+
)}
159+
</div>
160+
</div>
161+
)
162+
}
163+
}

Diff for: src/commands/index.ts

+5-2
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,10 @@ class TextAreaCommandOrchestrator implements CommandOrchestrator {
6565
}
6666
}
6767

68-
6968
export {
70-
getCommands, TextAreaCommandOrchestrator, TextAreaTextApi, getStateFromTextArea
69+
// Toolbars.
70+
bold, italic, strikethrough, hr, title, divider, link, quote, code, image,
71+
unorderedListCommand, orderedListCommand, checkedListCommand, codeEdit, codeLive, codePreview, fullscreen,
72+
// Tool method.
73+
getCommands, getStateFromTextArea, TextAreaCommandOrchestrator, TextAreaTextApi
7174
}

Diff for: src/components/Markdown/index.tsx

+8-3
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import React, { Component } from 'react';
22
import classnames from 'classnames';
3-
import ReactMarkdown from 'react-markdown';
3+
import ReactMarkdown, { ReactMarkdownProps } from 'react-markdown';
44
import Code from './Code';
55
import allowNode from './allowNode';
66
import { IProps } from '../../Type';
77

88

9-
export interface IMarkdownPreviewProps extends IProps, React.HTMLAttributes<HTMLDivElement> {}
9+
export interface IMarkdownPreviewProps extends IProps, Omit<ReactMarkdownProps, 'className'> {}
1010

1111
export interface IMarkdownPreviewState {
1212
value?: string;
@@ -17,9 +17,14 @@ export default class MarkdownPreview extends Component<IMarkdownPreviewProps, IM
1717
public constructor(props: IMarkdownPreviewProps) {
1818
super(props);
1919
this.state = {
20-
value: '',
20+
value: '' || props.source,
2121
};
2222
}
23+
UNSAFE_componentWillReceiveProps(nextProps: IMarkdownPreviewProps) {
24+
if (nextProps.source !== this.props.source) {
25+
this.setState({ value: nextProps.source });
26+
}
27+
}
2328
public renderHTML(mdStr?: string) {
2429
this.setState({ value: mdStr });
2530
}

Diff for: src/index.tsx

+17-158
Original file line numberDiff line numberDiff line change
@@ -1,162 +1,21 @@
1-
import React from 'react';
2-
import classnames from 'classnames';
3-
import { ReactMarkdownProps } from 'react-markdown';
4-
import { IProps, ICommand, CommandOrchestrator } from './Type';
5-
import TextArea, { ITextAreaProps} from './components/TextArea';
6-
import Toolbar from './components/Toolbar';
7-
import DragBar from './components/DragBar';
8-
import MarkdownPreview from './components/Markdown';
9-
import { getCommands, TextAreaCommandOrchestrator } from './commands';
10-
import './index.less';
11-
import './markdowncolor.less';
12-
import './markdown.less';
1+
import MDEditor, { IMDEditorProps, IMDEditorState } from './MDEditor';
2+
import * as commands from './commands';
3+
import * as MarkdownUtil from './utils/markdownUtils';
4+
import { ICommand, CommandOrchestrator, TextState, TextApi, TextRange } from './Type';
5+
import Markdown from './components/Markdown';
136

14-
export interface IMDEditorProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'onChange'>, IProps {
15-
/**
16-
* The Markdown value.
17-
*/
18-
value?: string;
19-
/**
20-
* Event handler for the `onChange` event.
21-
*/
22-
onChange?: (value: string) => void;
23-
/**
24-
* Can be used to make `Markdown Editor` focus itself on initialization. Defaults to on.
25-
* it will be set to true when either the source `textarea` is focused,
26-
* or it has an `autofocus` attribute and no other element is focused.
27-
*/
28-
autoFocus?: ITextAreaProps['autoFocus'];
29-
/**
30-
* The height of the editor.
31-
*/
32-
height?: React.CSSProperties['height'];
33-
/**
34-
* Show drag and drop tool. Set the height of the editor.
35-
*/
36-
visiableDragbar?: boolean;
37-
/**
38-
* Show markdown preview.
39-
*/
40-
preview?: 'live' | 'edit' | 'preview';
41-
fullscreen?: boolean;
42-
/**
43-
* Maximum drag height. `visiableDragbar=true`
44-
*/
45-
maxHeight?: number;
46-
previewOptions?: ReactMarkdownProps;
47-
/**
48-
* Minimum drag height. `visiableDragbar=true`
49-
*/
50-
minHeight?: number;
51-
/**
52-
* You can create your own commands or reuse existing commands.
53-
*/
54-
commands?: ICommand[];
55-
}
7+
MDEditor.Markdown = Markdown;
568

57-
export interface IMDEditorState {
58-
height: React.CSSProperties['height'];
59-
preview?: IMDEditorProps['preview'];
60-
fullscreen?: boolean;
9+
export {
10+
IMDEditorProps,
11+
IMDEditorState,
12+
MarkdownUtil,
13+
commands,
14+
ICommand,
15+
CommandOrchestrator,
16+
TextState,
17+
TextApi,
18+
TextRange,
6119
}
6220

63-
export default class MDEditor extends React.PureComponent<IMDEditorProps, IMDEditorState> {
64-
public static displayName = 'MDEditor';
65-
public preview = React.createRef<MarkdownPreview>();
66-
public textarea = React.createRef<TextArea>();
67-
public commandOrchestrator!: CommandOrchestrator;
68-
public static defaultProps: IMDEditorProps = {
69-
value: '',
70-
prefixCls: 'w-md-editor',
71-
height: 200,
72-
minHeight: 100,
73-
maxHeight: 1200,
74-
visiableDragbar: true,
75-
preview: 'live',
76-
fullscreen: false,
77-
commands: getCommands(),
78-
}
79-
public constructor(props: IMDEditorProps) {
80-
super(props);
81-
this.state = {
82-
height: props.height,
83-
preview: props.preview,
84-
fullscreen: props.fullscreen,
85-
};
86-
}
87-
public componentDidMount() {
88-
this.handleChange(this.props.value);
89-
this.commandOrchestrator = new TextAreaCommandOrchestrator(this.textarea.current!.text.current as HTMLTextAreaElement);
90-
}
91-
public UNSAFE_componentWillReceiveProps(nextProps: IMDEditorProps) {
92-
if (nextProps.preview !== this.props.preview) {
93-
this.setState({ preview: nextProps.preview });
94-
}
95-
if (nextProps.fullscreen !== this.props.fullscreen) {
96-
this.setState({ fullscreen: nextProps.fullscreen });
97-
}
98-
}
99-
private handleChange(mdStr?: string) {
100-
const { onChange } = this.props;
101-
this.preview.current!.renderHTML(mdStr);
102-
onChange && onChange(mdStr || '');
103-
}
104-
public handleCommand = (command: ICommand) => {
105-
if (command.keyCommand === 'preview') {
106-
this.setState({ preview: command.value as IMDEditorState['preview'] });
107-
}
108-
if (command.keyCommand === 'fullscreen') {
109-
this.setState({ fullscreen: !this.state.fullscreen });
110-
document.body.style.overflow = this.state.fullscreen ? 'initial' : 'hidden';
111-
}
112-
this.commandOrchestrator.executeCommand(command);
113-
}
114-
public render() {
115-
const { prefixCls, className, value, commands, height, visiableDragbar, preview, fullscreen, previewOptions, maxHeight, minHeight, autoFocus, onChange, ...other } = this.props;
116-
const cls = classnames(className, prefixCls, {
117-
[`${prefixCls}-show-${this.state.preview}`]: this.state.preview,
118-
[`${prefixCls}-fullscreen`]: this.state.fullscreen,
119-
});
120-
return (
121-
<div className={cls} style={{ height: this.state.fullscreen ? '100%' : this.state.height }} {...other}>
122-
<Toolbar
123-
active={{
124-
fullscreen: this.state.fullscreen,
125-
preview: this.state.preview,
126-
}}
127-
prefixCls={prefixCls} commands={commands}
128-
onCommand={this.handleCommand}
129-
/>
130-
<div
131-
className={`${prefixCls}-content`}
132-
style={{ height: this.state.fullscreen ? 'calc(100% - 29px)' : (this.state.height as number) - 29 }}
133-
>
134-
<TextArea
135-
ref={this.textarea}
136-
className={`${prefixCls}-input`}
137-
prefixCls={prefixCls}
138-
value={value}
139-
autoFocus={autoFocus}
140-
onChange={this.handleChange.bind(this)}
141-
/>
142-
<MarkdownPreview
143-
{...previewOptions}
144-
ref={this.preview}
145-
className={`${prefixCls}-preview`}
146-
/>
147-
{visiableDragbar && !this.state.fullscreen && (
148-
<DragBar
149-
prefixCls={prefixCls}
150-
height={this.state.height as number}
151-
maxHeight={maxHeight!}
152-
minHeight={minHeight!}
153-
onChange={(newHeight) => {
154-
this.setState({ height: newHeight });
155-
}}
156-
/>
157-
)}
158-
</div>
159-
</div>
160-
)
161-
}
162-
}
21+
export default MDEditor;

0 commit comments

Comments
 (0)