refactor: improved text layout for questions, answers, comments

This commit is contained in:
robin 2022-09-30 15:41:58 +08:00
parent af9aa53135
commit 7570d204f1
8 changed files with 65 additions and 23 deletions

View File

@ -43,7 +43,7 @@ const Index = ({ value }, ref) => {
return (
<div
ref={previewRef}
className="preview-wrap position-relative p-3 bg-light rounded text-break text-wrap mt-2"
className="preview-wrap position-relative p-3 bg-light rounded text-break text-wrap mt-2 fmt"
dangerouslySetInnerHTML={{ __html: html }}
/>
);

View File

@ -27,11 +27,12 @@ import {
Table,
UL,
} from './ToolBars';
import { createEditorUtils } from './utils';
import { createEditorUtils, htmlRender } from './utils';
import Viewer from './Viewer';
import { CodeMirrorEditor, IEditorContext } from './types';
import { EditorContext } from './EditorContext';
import Editor from './Editor';
import './index.scss';
export interface EditorRef {
@ -157,4 +158,5 @@ const MDEditor: ForwardRefRenderFunction<EditorRef, Props> = (
</>
);
};
export { htmlRender };
export default forwardRef(MDEditor);

View File

@ -1,6 +1,6 @@
import type { Editor, Position } from 'codemirror';
import type CodeMirror from 'codemirror';
import 'highlight.js/styles/github.css';
// import 'highlight.js/styles/github.css';
import 'katex/dist/katex.min.css';
export function createEditorUtils(
@ -82,8 +82,11 @@ export function createEditorUtils(
export function htmlRender(el: HTMLElement | null) {
if (!el) return;
import('mermaid').then(({ default: mermaid }) => {
el.querySelectorAll('.language-mermaid').forEach((pre, index) => {
mermaid.render(`theGraph${index}`, pre.textContent, function (svgCode) {
mermaid.initialize({ startOnLoad: false });
el.querySelectorAll('.language-mermaid').forEach((pre) => {
const flag = Date.now();
mermaid.render(`theGraph${flag}`, pre.textContent, function (svgCode) {
const p = document.createElement('p');
p.className = 'text-center';
p.innerHTML = svgCode;
@ -111,9 +114,9 @@ export function htmlRender(el: HTMLElement | null) {
});
},
);
import('highlight.js').then(({ default: highlight }) => {
el.querySelectorAll('pre code').forEach((code) => {
highlight.highlightElement(code as HTMLElement);
});
});
// import('highlight.js').then(({ default: highlight }) => {
// el.querySelectorAll('pre code').forEach((code) => {
// highlight.highlightElement(code as HTMLElement);
// });
// });
}

View File

@ -1,5 +1,5 @@
import Avatar from './Avatar';
import Editor, { EditorRef } from './Editor';
import Editor, { EditorRef, htmlRender } from './Editor';
import Header from './Header';
import Footer from './Footer';
import Icon from './Icon';
@ -52,5 +52,6 @@ export {
Empty,
BaseUserCard,
FollowingTags,
htmlRender,
};
export type { EditorRef };

View File

@ -139,3 +139,32 @@ a {
.object-fit-contain {
object-fit: contain;
}
.fmt {
width: 100%;
img {
max-width: 100%;
}
p {
word-break: break-all;
> code {
background-color: #e9ecef;
color: #212529;
padding: 2px 4px;
border-radius: 0.25rem;
}
}
pre {
background-color: #e9ecef;
border-radius: 0.25rem;
padding: 1rem;
}
blockquote {
border-left: 0.25rem solid #ced4da;
padding: 1rem;
color: #6c757d;
background-color: #e9ecef;
> p:last-child {
margin-bottom: 0;
}
}
}

View File

@ -9,6 +9,7 @@ import {
Icon,
Comment,
FormatTime,
htmlRender,
} from '@answer/components';
import { acceptanceAnswer } from '@answer/api';
import { scrollTop } from '@answer/utils';
@ -44,13 +45,17 @@ const Index: FC<Props> = ({
};
useEffect(() => {
if (aid === data.id && answerRef?.current) {
if (!answerRef?.current) {
return;
}
if (aid === data.id) {
setTimeout(() => {
const element = answerRef.current;
scrollTop(element);
}, 100);
}
}, [data.id, answerRef]);
htmlRender(answerRef.current.querySelector('.fmt'));
}, [data.id, answerRef.current]);
if (!data?.id) {
return null;
}

View File

@ -1,4 +1,4 @@
import { memo, FC, useState, useEffect } from 'react';
import { memo, FC, useState, useEffect, useRef } from 'react';
import { Link } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import { Row, Col, Button } from 'react-bootstrap';
@ -10,6 +10,7 @@ import {
UserCard,
Comment,
FormatTime,
htmlRender,
} from '@answer/components';
import { formatCount } from '@answer/utils';
import { following } from '@answer/api';
@ -25,6 +26,7 @@ const Index: FC<Props> = ({ data, initPage, hasAnswer }) => {
keyPrefix: 'question_detail',
});
const [followed, setFollowed] = useState(data?.is_followed);
const ref = useRef<HTMLDivElement>(null);
const handleFollow = (e) => {
e.preventDefault();
@ -42,6 +44,14 @@ const Index: FC<Props> = ({ data, initPage, hasAnswer }) => {
}
}, [data]);
useEffect(() => {
if (!ref.current) {
return;
}
htmlRender(ref.current);
}, [ref.current]);
if (!data?.id) {
return null;
}
@ -94,6 +104,7 @@ const Index: FC<Props> = ({ data, initPage, hasAnswer }) => {
})}
</div>
<article
ref={ref}
dangerouslySetInnerHTML={{ __html: data?.html }}
className="fmt text-break text-wrap"
/>

View File

@ -1,12 +1,3 @@
.fmt {
img {
max-width: 100%;
}
p {
word-break: break-all;
}
}
.answer-item {
border-top: 1px solid rgba(33, 37, 41, 0.25);
}