Skip to content

Commit 7b918ad

Browse files
committed
Optimize markdown editor
1 parent 37fcc78 commit 7b918ad

File tree

6 files changed

+113
-77
lines changed

6 files changed

+113
-77
lines changed

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
"@testing-library/react": "^9.3.2",
2020
"@testing-library/user-event": "^7.1.2",
2121
"@types/aws-lambda": "^8.10.39",
22-
"@types/codemirror": "^0.0.81",
22+
"@types/codemirror": "^0.0.84",
2323
"@types/date-fns": "^2.6.0",
2424
"@types/ioredis": "^4.14.3",
2525
"@types/jest": "^24.0.0",
@@ -59,7 +59,7 @@
5959
"babel-preset-react-app": "^9.1.0",
6060
"camelcase": "^5.3.1",
6161
"case-sensitive-paths-webpack-plugin": "2.2.0",
62-
"codemirror": "^5.49.2",
62+
"codemirror": "^5.51.0",
6363
"copy-webpack-plugin": "^5.1.1",
6464
"core-js": "^3.6.4",
6565
"css-loader": "3.2.0",

src/components/common/MarkdownRender.tsx

Lines changed: 23 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import embedPlugin from '../../lib/remark/embedPlugin';
1111
import { loadScript, ssrEnabled } from '../../lib/utils';
1212
import media from '../../lib/styles/media';
1313
import parse from 'html-react-parser';
14+
import { throttle } from 'throttle-debounce';
1415

1516
export interface MarkdownRenderProps {
1617
markdown: string;
@@ -79,6 +80,13 @@ const MarkdownRenderBlock = styled.div`
7980
`;
8081

8182
const { useState, useEffect } = React;
83+
84+
type RenderedElement =
85+
| string
86+
| React.DetailedReactHTMLElement<{}, HTMLElement>
87+
| Array<React.DetailedReactHTMLElement<{}, HTMLElement>>
88+
| null;
89+
8290
const MarkdownRender: React.FC<MarkdownRenderProps> = ({
8391
markdown,
8492
codeTheme = 'atom-one-light',
@@ -95,7 +103,16 @@ const MarkdownRender: React.FC<MarkdownRenderProps> = ({
95103
.toString()
96104
: '';
97105

98-
const [html, setHtml] = useState(initialHtml);
106+
const [element, setElement] = useState<RenderedElement>(
107+
ssrEnabled ? parse(initialHtml) : null,
108+
);
109+
110+
const applyElement = React.useMemo(() => {
111+
return throttle(250, (el: any) => {
112+
setElement(el);
113+
});
114+
}, []);
115+
99116
useEffect(() => {
100117
remark()
101118
.use(breaks)
@@ -105,20 +122,20 @@ const MarkdownRender: React.FC<MarkdownRenderProps> = ({
105122
.use(slug)
106123
.process(markdown, (err: any, file: any) => {
107124
const html = String(file);
108-
setHtml(html);
125+
109126
if (onConvertFinish) {
110127
onConvertFinish(html);
111128
}
112-
113129
// load twitter script if needed
114130
if (html.indexOf('class="twitter-tweet"') !== -1) {
115131
// if (window && (window as any).twttr) return;
116132
loadScript('https://platform.twitter.com/widgets.js');
117133
}
118-
});
119-
}, [markdown, onConvertFinish]);
134+
const el = parse(html);
120135

121-
const element = parse(html);
136+
applyElement(el);
137+
});
138+
}, [applyElement, markdown, onConvertFinish]);
122139

123140
return (
124141
<Typography>
@@ -127,38 +144,4 @@ const MarkdownRender: React.FC<MarkdownRenderProps> = ({
127144
);
128145
};
129146

130-
// export default class MarkdownRender extends React.Component<
131-
// MarkdownRenderProps,
132-
// any
133-
// > {
134-
// state = {
135-
// html: '',
136-
// };
137-
// process = () => {
138-
// remark()
139-
// .use(prismPlugin)
140-
// .use(htmlPlugin)
141-
// .process(this.props.markdown, (err: any, file: any) => {
142-
// console.log(err);
143-
// console.log(file);
144-
// const html = String(file);
145-
// console.log(html);
146-
// });
147-
// };
148-
149-
// componentDidMount() {
150-
// this.process();
151-
// }
152-
153-
// componentDidUpdate(prevProps: MarkdownRenderProps) {
154-
// if (prevProps.markdown !== this.props.markdown) {
155-
// this.process();
156-
// }
157-
// }
158-
159-
// public render() {
160-
// return <div />;
161-
// }
162-
// }
163-
164147
export default React.memo(MarkdownRender);

src/components/write/MarkdownPreview.tsx

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,6 @@ import palette from '../../lib/styles/palette';
55

66
const MarkdownPreviewBlock = styled.div`
77
word-break: break-word;
8-
&::-webkit-scrollbar {
9-
border-radius: 3px;
10-
width: 6px;
11-
&:hover {
12-
width: 16px;
13-
}
14-
background: ${palette.gray1};
15-
}
16-
17-
&::-webkit-scrollbar-thumb {
18-
background: ${palette.gray9};
19-
/* -webkit-box-shadow: 0px 1px 2px rgba(0, 0, 0, 0.75); */
20-
}
21-
228
padding: 3rem;
239
flex: 1;
2410
overflow-y: auto;
@@ -43,7 +29,7 @@ const MarkdownPreview: React.FC<MarkdownPreviewProps> = ({
4329
className,
4430
}) => {
4531
return (
46-
<MarkdownPreviewBlock>
32+
<MarkdownPreviewBlock id="preview">
4733
<Title>{title}</Title>
4834
<MarkdownRender markdown={markdown} />
4935
</MarkdownPreviewBlock>

src/components/write/Toolbar.tsx

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,20 +32,22 @@ const ToolbarBlock = styled.div<{
3232
z-index: ${zIndexes.Toolbar};
3333
transition: all 0.125s ease-in;
3434
flex-wrap: wrap;
35-
${props =>
36-
props.shadow &&
37-
css`
38-
box-shadow: 0px 2px 8px rgba(0, 0, 0, 0.09);
39-
`}
35+
4036
${props =>
4137
props.forMarkdown &&
4238
css`
43-
margin-top: 2rem;
4439
margin-bottom: 1rem;
4540
padding-left: 3rem;
4641
padding-right: 3rem;
4742
width: auto;
4843
`}
44+
45+
${props =>
46+
props.shadow &&
47+
css`
48+
box-shadow: 0px 2px 8px rgba(0, 0, 0, 0.09);
49+
margin-bottom: 0;
50+
`}
4951
`;
5052

5153
const ToolbarGroup = styled.div`

src/components/write/WriteMarkdownEditor.tsx

Lines changed: 71 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { detectJSDOM, ssrEnabled } from '../../lib/utils';
1212
import AskChangeEditor from './AskChangeEditor';
1313
import { WriteMode } from '../../modules/write';
1414
import zIndexes from '../../lib/styles/zIndexes';
15+
import { useSpring, animated } from 'react-spring';
1516
require('codemirror/mode/markdown/markdown');
1617
require('codemirror/mode/javascript/javascript');
1718
require('codemirror/mode/jsx/jsx');
@@ -40,14 +41,15 @@ type MarkdownEditorState = {
4041
};
4142
askChangeEditor: boolean;
4243
clientWidth: number;
44+
hideUpper: boolean;
4345
};
4446

4547
const MarkdownEditorBlock = styled.div`
4648
height: 100%;
4749
display: flex;
4850
flex-direction: column;
49-
overflow-y: auto;
5051
position: relative;
52+
5153
&::-webkit-scrollbar {
5254
border-radius: 3px;
5355
width: 6px;
@@ -64,13 +66,25 @@ const MarkdownEditorBlock = styled.div`
6466
}
6567
6668
& > .wrapper {
67-
padding-top: 3rem;
69+
min-height: 0;
6870
padding-bottom: 4rem;
6971
flex: 1;
7072
display: flex;
7173
flex-direction: column;
7274
}
75+
76+
.CodeMirror-lines {
77+
padding: 4px 0; /* Vertical padding around content */
78+
padding-bottom: 3rem;
79+
}
80+
81+
.CodeMirror pre.CodeMirror-line,
82+
.CodeMirror pre.CodeMirror-line-like {
83+
padding: 0 3rem; /* Horizontal padding of content */
84+
}
85+
7386
.CodeMirror {
87+
min-height: 0;
7488
flex: 1;
7589
font-size: 1.125rem;
7690
line-height: 1.5;
@@ -116,12 +130,14 @@ const HorizontalBar = styled.div`
116130
`;
117131

118132
const PaddingWrapper = styled.div`
133+
padding-top: 2rem;
119134
padding-left: 3rem;
120135
padding-right: 3rem;
121136
`;
122137

123138
const MarkdownWrapper = styled.div`
124139
flex: 1;
140+
min-height: 0;
125141
display: flex;
126142
flex-direction: column;
127143
`;
@@ -176,6 +192,28 @@ const checkEmbed = (text: string) => {
176192
return null;
177193
};
178194

195+
function WriterHead({
196+
hide,
197+
children,
198+
}: {
199+
hide: boolean;
200+
children: React.ReactNode;
201+
}) {
202+
const springStyle = useSpring({
203+
maxHeight: hide ? 0 : window.screen.height * 0.5,
204+
opacity: hide ? 0 : 1,
205+
config: {
206+
duration: 300,
207+
},
208+
});
209+
210+
return (
211+
<animated.div style={springStyle}>
212+
<PaddingWrapper>{children}</PaddingWrapper>
213+
</animated.div>
214+
);
215+
}
216+
179217
export default class WriteMarkdownEditor extends React.Component<
180218
MarkdownEditorProps,
181219
MarkdownEditorState
@@ -194,9 +232,12 @@ export default class WriteMarkdownEditor extends React.Component<
194232
},
195233
askChangeEditor: false,
196234
clientWidth: 0,
235+
hideUpper: false,
197236
};
198237
codemirror: EditorFromTextArea | null = null;
199238

239+
ignore = false;
240+
200241
initialize = () => {
201242
if (!this.editorElement.current) return;
202243

@@ -213,6 +254,30 @@ export default class WriteMarkdownEditor extends React.Component<
213254
this.codemirror.on('change', cm => {
214255
this.props.onChangeMarkdown(cm.getValue());
215256
this.stickToBottomIfNeeded();
257+
const doc = cm.getDoc();
258+
259+
// scroll to bottom when editing last 5
260+
const { line } = doc.getCursor();
261+
const last = doc.lastLine();
262+
if (last - line < 5) {
263+
const preview = document.getElementById('preview');
264+
if (!preview) return;
265+
preview.scrollTop = preview.scrollHeight;
266+
}
267+
});
268+
269+
this.codemirror.on('scroll', cm => {
270+
const info = cm.getScrollInfo();
271+
272+
if (info.top > 0 && info.height > window.screen.height) {
273+
this.setState({ hideUpper: true });
274+
} else {
275+
this.setState({ hideUpper: false });
276+
}
277+
});
278+
279+
this.codemirror.on('mousewheel', cm => {
280+
console.log(cm.getScrollInfo());
216281
});
217282

218283
this.codemirror.on('paste', ((editor: any, e: any) => {
@@ -260,7 +325,7 @@ export default class WriteMarkdownEditor extends React.Component<
260325
handleScroll = (e: React.UIEvent<HTMLDivElement>) => {
261326
const { shadow } = this.state;
262327
const nextShadow = e.currentTarget.scrollTop > this.toolbarTop;
263-
console.log(e.currentTarget.scrollTop, this.toolbarTop);
328+
// console.log(e.currentTarget.scrollTop, this.toolbarTop);
264329
if (shadow !== nextShadow) {
265330
this.setState({
266331
shadow: nextShadow,
@@ -737,7 +802,7 @@ ${selected}
737802
data-testid="codemirror"
738803
>
739804
<div className="wrapper">
740-
<PaddingWrapper>
805+
<WriterHead hide={this.state.hideUpper}>
741806
{ssrEnabled ? (
742807
<TitleTextareaForSSR placeholder="제목을 입력하세요" rows={1} />
743808
) : (
@@ -749,9 +814,9 @@ ${selected}
749814
)}
750815
<HorizontalBar />
751816
{tagInput}
752-
</PaddingWrapper>
817+
</WriterHead>
753818
<Toolbar
754-
shadow={shadow}
819+
shadow={shadow || this.state.hideUpper}
755820
mode="MARKDOWN"
756821
onClick={this.handleToolbarClick}
757822
onConvert={this.handleAskConvert}

yarn.lock

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1470,10 +1470,10 @@
14701470
"@types/connect" "*"
14711471
"@types/node" "*"
14721472

1473-
"@types/codemirror@^0.0.81":
1474-
version "0.0.81"
1475-
resolved "https://registry.yarnpkg.com/@types/codemirror/-/codemirror-0.0.81.tgz#3cea2c1006406973d4aad212e617638cbfc2965b"
1476-
integrity sha512-QoeIb9tn/TZp0IzNyMKlcwHR6pHxNoIW9mKqF7asAsj+AWqpFOEUsxia756L6WKBQNk2psHpu+aFuIFqo3Ac0A==
1473+
"@types/codemirror@^0.0.84":
1474+
version "0.0.84"
1475+
resolved "https://registry.yarnpkg.com/@types/codemirror/-/codemirror-0.0.84.tgz#b0cfca79ccdfd45ffe1f737668276a31b3149ebd"
1476+
integrity sha512-W78ZhfHPGYoYGCpAcEa268QUU3CVMA8BwcybxUMhEs2v5Rj58/7lGeRh3P7tauWRnSg3Pyn5ymw2lt65AyrVUw==
14771477
dependencies:
14781478
"@types/tern" "*"
14791479

@@ -3580,10 +3580,10 @@ code-point-at@^1.0.0:
35803580
resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77"
35813581
integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=
35823582

3583-
codemirror@^5.49.2:
3584-
version "5.49.2"
3585-
resolved "https://registry.yarnpkg.com/codemirror/-/codemirror-5.49.2.tgz#c84fdaf11b19803f828b0c67060c7bc6d154ccad"
3586-
integrity sha512-dwJ2HRPHm8w51WB5YTF9J7m6Z5dtkqbU9ntMZ1dqXyFB9IpjoUFDj80ahRVEoVanfIp6pfASJbOlbWdEf8FOzQ==
3583+
codemirror@^5.51.0:
3584+
version "5.51.0"
3585+
resolved "https://registry.yarnpkg.com/codemirror/-/codemirror-5.51.0.tgz#7746caaf5223e68f5c55ea11e2f3cc82a9a3929e"
3586+
integrity sha512-vyuYYRv3eXL0SCuZA4spRFlKNzQAewHcipRQCOKgRy7VNAvZxTKzbItdbCl4S5AgPZ5g3WkHp+ibWQwv9TLG7Q==
35873587

35883588
collapse-white-space@^1.0.0, collapse-white-space@^1.0.2:
35893589
version "1.0.5"

0 commit comments

Comments
 (0)