Skip to content
This repository was archived by the owner on Mar 13, 2025. It is now read-only.

Commit a1ead8e

Browse files
committed
fix: add markdown editor
1 parent 1266d9c commit a1ead8e

File tree

11 files changed

+179
-42
lines changed

11 files changed

+179
-42
lines changed

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@
5656
"dependencies": {
5757
"@popperjs/core": "^2.5.4",
5858
"@reach/router": "^1.3.4",
59-
"@toast-ui/react-editor": "^2.5.1",
59+
"@toast-ui/editor": "^2.5.1",
6060
"axios": "^0.21.0",
6161
"classnames": "^2.2.6",
6262
"express": "^4.17.1",

src/components/FormField/index.jsx

+5-5
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { Field } from "react-final-form";
99
import { FORM_FIELD_TYPE } from "../../constants";
1010
import TextInput from "../../components/TextInput";
1111
import TextArea from "../../components/TextArea";
12-
import TuiEditor from "../../components/TuiEditor";
12+
import MarkdownEditor from "../../components/MarkdownEditor";
1313
import ReactSelect from "../../components/ReactSelect";
1414
import DateInput from "../../components/DateInput";
1515
import "./styles.module.scss";
@@ -60,13 +60,13 @@ const FormField = ({ field }) => {
6060
step={field.step}
6161
/>
6262
)}
63-
{field.type === FORM_FIELD_TYPE.TUIEDITOR && (
64-
<TuiEditor
63+
{field.type === FORM_FIELD_TYPE.MARKDOWNEDITOR && (
64+
<MarkdownEditor
6565
placeholder={field.placeholder}
6666
value={input?.value ?? ""}
6767
onChange={input.onChange}
68-
// onBlur={input.onBlur}
69-
// onFocus={input.onFocus}
68+
onBlur={input.onBlur}
69+
onFocus={input.onFocus}
7070
className={meta.error && meta.touched ? "error" : ""}
7171
/>
7272
)}
+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
* MarkdownEditor
3+
*/
4+
5+
import React, { useState, useCallback, useRef } from "react";
6+
import PropTypes from "prop-types";
7+
import cn from "classnames";
8+
import TuiEditor from "../TuiEditor";
9+
import styles from "./styles.module.scss";
10+
11+
const MarkdownEditor = (props) => {
12+
const editorElement = useRef(null);
13+
14+
const onChange = useCallback(() => {
15+
const mk = editorElement.current.editorInst.getMarkdown();
16+
props.onChange(mk);
17+
}, []);
18+
19+
return (
20+
<div className={cn(styles["editor-container"], props.className)}>
21+
<TuiEditor
22+
{...props}
23+
ref={editorElement}
24+
onChange={onChange}
25+
initialValue={props.value}
26+
/>
27+
</div>
28+
);
29+
};
30+
31+
MarkdownEditor.propTypes = {
32+
value: PropTypes.string,
33+
className: PropTypes.string,
34+
onChange: PropTypes.func,
35+
onFocus: PropTypes.func,
36+
onBlur: PropTypes.func,
37+
};
38+
39+
export default MarkdownEditor;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/*
2+
* MarkdownViewer
3+
*/
4+
5+
import React, { useState } from "react";
6+
import PropTypes from "prop-types";
7+
import TuiEditorViewer from "../TuiEditorViewer";
8+
9+
const MarkdownViewer = (props) => (
10+
<TuiEditorViewer initialValue={props.value} />
11+
);
12+
13+
MarkdownViewer.propTypes = {
14+
value: PropTypes.string,
15+
};
16+
17+
export default MarkdownViewer;

src/components/TuiEditor/index.jsx

+61-24
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,68 @@
11
/*
22
* TuiEditor
3+
* wrap toast-ui editor with react
34
*/
4-
5-
import React, { useState } from "react";
5+
import React from "react";
66
import PropTypes from "prop-types";
7-
import cn from "classnames";
8-
import { Editor } from "@toast-ui/react-editor";
9-
import styles from "./styles.module.scss";
7+
import Editor from "@toast-ui/editor";
108

11-
const TuiEditor = (props) => {
12-
const [editorElement, setEditorElement] = useState(null);
13-
const onChange = () => {
14-
const mk = editorElement.editorInst.getMarkdown();
15-
props.onChange(mk);
16-
};
17-
return (
18-
<div className={cn(styles["editor-container"], props.className)}>
19-
<Editor
20-
{...props}
21-
ref={setEditorElement}
22-
onChange={onChange}
23-
initialValue={props.value}
24-
/>
25-
</div>
26-
);
27-
};
9+
class TuiEditor extends React.Component {
10+
constructor(props) {
11+
super(props);
12+
this.rootEl = React.createRef();
13+
this.editorInst = null;
14+
}
15+
16+
getRootElement() {
17+
return this.rootEl.current;
18+
}
19+
20+
getInstance() {
21+
return this.editorInst;
22+
}
23+
24+
bindEventHandlers(props) {
25+
Object.keys(this.props)
26+
.filter((key) => /^on[A-Z][a-zA-Z]+/.test(key))
27+
.forEach((key) => {
28+
const eventName = key[2].toLowerCase() + key.slice(3);
29+
// off function has issue
30+
// when add `onFocus` function, the headings popup will not hide automatically
31+
// this.editorInst.off(eventName, props[key]);
32+
this.editorInst.on(eventName, props[key]);
33+
});
34+
}
35+
36+
componentDidMount() {
37+
this.editorInst = new Editor({
38+
el: this.rootEl.current,
39+
...this.props,
40+
});
41+
42+
this.bindEventHandlers(this.props);
43+
}
44+
45+
shouldComponentUpdate(nextProps) {
46+
const instance = this.getInstance();
47+
const { height, previewStyle } = nextProps;
48+
49+
if (this.props.height !== height) {
50+
instance.height(height);
51+
}
52+
53+
if (this.props.previewStyle !== previewStyle) {
54+
instance.changePreviewStyle(previewStyle);
55+
}
56+
57+
this.bindEventHandlers(nextProps, this.props);
58+
59+
return false;
60+
}
61+
62+
render() {
63+
return <div ref={this.rootEl} />;
64+
}
65+
}
2866

2967
TuiEditor.defaultProps = {
3068
height: "320px",
@@ -44,8 +82,7 @@ TuiEditor.defaultProps = {
4482

4583
TuiEditor.propTypes = {
4684
// Editor's initial value
47-
value: PropTypes.string,
48-
className: PropTypes.string,
85+
initialValue: PropTypes.string,
4986
// Markdown editor's preview style (tab, vertical)
5087
previewStyle: PropTypes.string.isRequired,
5188
// Editor's height style value. Height is applied as border-box ex) '300px', '100%', 'auto'
+51-7
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,59 @@
11
/*
22
* TuiEditorViewer
33
*/
4-
5-
import React, { useState } from "react";
4+
import React from "react";
65
import PropTypes from "prop-types";
7-
import { Viewer } from "@toast-ui/react-editor";
6+
import Viewer from "@toast-ui/editor/dist/toastui-editor-viewer";
7+
8+
class TuiViewer extends React.Component {
9+
constructor(props) {
10+
super(props);
11+
this.rootEl = React.createRef();
12+
this.viewerInst = null;
13+
}
14+
15+
getRootElement() {
16+
return this.rootEl.current;
17+
}
18+
19+
getInstance() {
20+
return this.viewerInst;
21+
}
22+
23+
bindEventHandlers(props) {
24+
Object.keys(this.props)
25+
.filter((key) => /^on[A-Z][a-zA-Z]+/.test(key))
26+
.forEach((key) => {
27+
const eventName = key[2].toLowerCase() + key.slice(3);
28+
// off function has issue
29+
// when add `onFocus` function, the headings popup will not hide automatically
30+
// this.editorInst.off(eventName, props[key]);
31+
this.viewerInst.on(eventName, props[key]);
32+
});
33+
}
34+
35+
componentDidMount() {
36+
this.viewerInst = new Viewer({
37+
el: this.rootEl.current,
38+
...this.props,
39+
});
40+
41+
this.bindEventHandlers(this.props);
42+
}
43+
44+
shouldComponentUpdate(nextProps) {
45+
this.bindEventHandlers(nextProps, this.props);
46+
47+
return false;
48+
}
849

9-
const TuiEditorViewer = (props) => <Viewer initialValue={props.value} />;
50+
render() {
51+
return <div ref={this.rootEl} />;
52+
}
53+
}
1054

11-
TuiEditorViewer.propTypes = {
12-
value: PropTypes.string,
55+
TuiViewer.propTypes = {
56+
initialValue: PropTypes.string,
1357
};
1458

15-
export default TuiEditorViewer;
59+
export default TuiViewer;

src/constants/index.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ export const ACTION_TYPE = {
168168
export const FORM_FIELD_TYPE = {
169169
TEXT: "text",
170170
TEXTAREA: "textarea",
171-
TUIEDITOR: "tuieditor",
171+
MARKDOWNEDITOR: "markdowneditor",
172172
NUMBER: "number",
173173
SELECT: "select",
174174
MULTISELECT: "multiselect",

src/routes/JobDetails/index.jsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import { useData } from "hooks/useData";
1212
import { getJobById } from "services/jobs";
1313
import { getSkills } from "services/skills";
1414
import LoadingIndicator from "../../components/LoadingIndicator";
15-
import TuiEditorViewer from "../../components/TuiEditorViewer";
15+
import MarkdownEditorViewer from "../../components/MarkdownEditorViewer";
1616
import withAuthentication from "../../hoc/withAuthentication";
1717
import DataItem from "../../components/DataItem";
1818
import IconSkill from "../../assets/images/icon-skill.svg";
@@ -65,7 +65,7 @@ const JobDetails = ({ teamId, jobId }) => {
6565
{job.title}
6666
</DataItem>
6767
<DataItem title="Job Description" icon={<IconDescription />}>
68-
<TuiEditorViewer value={job.description} />
68+
<MarkdownEditorViewer value={job.description} />
6969
</DataItem>
7070
<DataItem title="Number of Openings" icon={<IconOpenings />}>
7171
{job.numPositions}

src/routes/JobForm/utils.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ export const getEditJobConfig = (skillOptions, onSubmit) => {
4242
},
4343
{
4444
label: "Job Description",
45-
type: FORM_FIELD_TYPE.TUIEDITOR,
45+
type: FORM_FIELD_TYPE.MARKDOWNEDITOR,
4646
name: "description",
4747
placeholder: "Job Description",
4848
},

src/styles/main.vendor.scss

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,6 @@
55
@import "~react-responsive-modal/styles";
66
@import "~react-loader-spinner/dist/loader/css/react-spinner-loader.css";
77

8-
// toast-ui.react-editor styles
8+
// toast-ui.editor styles
99
@import "~codemirror/lib/codemirror.css";
1010
@import "~@toast-ui/editor/dist/toastui-editor.css"

0 commit comments

Comments
 (0)