mirror of https://gitee.com/answerdev/answer.git
Merge pull request #295 from answerdev/feat/1.0.8/ui
fix: use htmlRender control rich-text style and delet dompurify html-…
This commit is contained in:
commit
8b4e0c928e
|
@ -22,9 +22,7 @@
|
|||
"copy-to-clipboard": "^3.3.2",
|
||||
"dayjs": "^1.11.5",
|
||||
"diff": "^5.1.0",
|
||||
"dompurify": "^2.4.3",
|
||||
"emoji-regex": "^10.2.1",
|
||||
"html-react-parser": "^3.0.8",
|
||||
"i18next": "^21.9.0",
|
||||
"katex": "^0.16.2",
|
||||
"lodash": "^4.17.21",
|
||||
|
|
|
@ -29,7 +29,6 @@ specifiers:
|
|||
customize-cra: ^1.0.0
|
||||
dayjs: ^1.11.5
|
||||
diff: ^5.1.0
|
||||
dompurify: ^2.4.3
|
||||
emoji-regex: ^10.2.1
|
||||
eslint: ^8.0.1
|
||||
eslint-config-airbnb: ^19.0.4
|
||||
|
@ -43,7 +42,6 @@ specifiers:
|
|||
eslint-plugin-promise: ^6.0.0
|
||||
eslint-plugin-react: ^7.30.1
|
||||
eslint-plugin-react-hooks: ^4.6.0
|
||||
html-react-parser: ^3.0.8
|
||||
husky: ^8.0.1
|
||||
i18next: ^21.9.0
|
||||
katex: ^0.16.2
|
||||
|
@ -83,9 +81,7 @@ dependencies:
|
|||
copy-to-clipboard: 3.3.2
|
||||
dayjs: 1.11.5
|
||||
diff: 5.1.0
|
||||
dompurify: registry.npmjs.org/dompurify/2.4.3
|
||||
emoji-regex: 10.2.1
|
||||
html-react-parser: registry.npmjs.org/html-react-parser/3.0.8_react@18.2.0
|
||||
i18next: 21.9.2
|
||||
katex: 0.16.2
|
||||
lodash: 4.17.21
|
||||
|
@ -11303,16 +11299,6 @@ packages:
|
|||
domhandler: registry.npmjs.org/domhandler/4.3.1
|
||||
entities: registry.npmjs.org/entities/2.2.0
|
||||
|
||||
registry.npmjs.org/dom-serializer/2.0.0:
|
||||
resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==, registry: https://registry.yarnpkg.com/, tarball: https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz}
|
||||
name: dom-serializer
|
||||
version: 2.0.0
|
||||
dependencies:
|
||||
domelementtype: registry.npmjs.org/domelementtype/2.3.0
|
||||
domhandler: registry.npmjs.org/domhandler/5.0.3
|
||||
entities: registry.npmjs.org/entities/4.4.0
|
||||
dev: false
|
||||
|
||||
registry.npmjs.org/domelementtype/1.3.1:
|
||||
resolution: {integrity: sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==, registry: https://registry.yarnpkg.com/, tarball: https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz}
|
||||
name: domelementtype
|
||||
|
@ -11331,27 +11317,12 @@ packages:
|
|||
dependencies:
|
||||
domelementtype: registry.npmjs.org/domelementtype/2.3.0
|
||||
|
||||
registry.npmjs.org/domhandler/5.0.3:
|
||||
resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==, registry: https://registry.yarnpkg.com/, tarball: https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz}
|
||||
name: domhandler
|
||||
version: 5.0.3
|
||||
engines: {node: '>= 4'}
|
||||
dependencies:
|
||||
domelementtype: registry.npmjs.org/domelementtype/2.3.0
|
||||
dev: false
|
||||
|
||||
registry.npmjs.org/dompurify/2.4.0:
|
||||
resolution: {integrity: sha512-Be9tbQMZds4a3C6xTmz68NlMfeONA//4dOavl/1rNw50E+/QO0KVpbcU0PcaW0nsQxurXls9ZocqFxk8R2mWEA==, registry: https://registry.yarnpkg.com/, tarball: https://registry.npmjs.org/dompurify/-/dompurify-2.4.0.tgz}
|
||||
name: dompurify
|
||||
version: 2.4.0
|
||||
dev: false
|
||||
|
||||
registry.npmjs.org/dompurify/2.4.3:
|
||||
resolution: {integrity: sha512-q6QaLcakcRjebxjg8/+NP+h0rPfatOgOzc46Fst9VAA3jF2ApfKBNKMzdP4DYTqtUMXSCd5pRS/8Po/OmoCHZQ==, registry: https://registry.yarnpkg.com/, tarball: https://registry.npmjs.org/dompurify/-/dompurify-2.4.3.tgz}
|
||||
name: dompurify
|
||||
version: 2.4.3
|
||||
dev: false
|
||||
|
||||
registry.npmjs.org/domutils/1.7.0:
|
||||
resolution: {integrity: sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==, registry: https://registry.yarnpkg.com/, tarball: https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz}
|
||||
name: domutils
|
||||
|
@ -11369,52 +11340,11 @@ packages:
|
|||
domelementtype: registry.npmjs.org/domelementtype/2.3.0
|
||||
domhandler: registry.npmjs.org/domhandler/4.3.1
|
||||
|
||||
registry.npmjs.org/domutils/3.0.1:
|
||||
resolution: {integrity: sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q==, registry: https://registry.yarnpkg.com/, tarball: https://registry.npmjs.org/domutils/-/domutils-3.0.1.tgz}
|
||||
name: domutils
|
||||
version: 3.0.1
|
||||
dependencies:
|
||||
dom-serializer: registry.npmjs.org/dom-serializer/2.0.0
|
||||
domelementtype: registry.npmjs.org/domelementtype/2.3.0
|
||||
domhandler: registry.npmjs.org/domhandler/5.0.3
|
||||
dev: false
|
||||
|
||||
registry.npmjs.org/entities/2.2.0:
|
||||
resolution: {integrity: sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==, registry: https://registry.yarnpkg.com/, tarball: https://registry.npmjs.org/entities/-/entities-2.2.0.tgz}
|
||||
name: entities
|
||||
version: 2.2.0
|
||||
|
||||
registry.npmjs.org/entities/4.4.0:
|
||||
resolution: {integrity: sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA==, registry: https://registry.yarnpkg.com/, tarball: https://registry.npmjs.org/entities/-/entities-4.4.0.tgz}
|
||||
name: entities
|
||||
version: 4.4.0
|
||||
engines: {node: '>=0.12'}
|
||||
dev: false
|
||||
|
||||
registry.npmjs.org/html-dom-parser/3.1.3:
|
||||
resolution: {integrity: sha512-fI0yyNlIeSboxU+jnrA4v8qj4+M8SI9/q6AKYdwCY2qki22UtKCDTxvagHniECu7sa5/o2zFRdLleA67035lsA==, registry: https://registry.yarnpkg.com/, tarball: https://registry.npmjs.org/html-dom-parser/-/html-dom-parser-3.1.3.tgz}
|
||||
name: html-dom-parser
|
||||
version: 3.1.3
|
||||
dependencies:
|
||||
domhandler: registry.npmjs.org/domhandler/5.0.3
|
||||
htmlparser2: registry.npmjs.org/htmlparser2/8.0.1
|
||||
dev: false
|
||||
|
||||
registry.npmjs.org/html-react-parser/3.0.8_react@18.2.0:
|
||||
resolution: {integrity: sha512-eIxPq/3Ja3+nZkx6X/BNpFy0lNuW+v3V4nzABzuL8KkRVdYY/2KyXO42epKonsEVVRSBxm2zsxWcOkT6fYL+iQ==, registry: https://registry.yarnpkg.com/, tarball: https://registry.npmjs.org/html-react-parser/-/html-react-parser-3.0.8.tgz}
|
||||
id: registry.npmjs.org/html-react-parser/3.0.8
|
||||
name: html-react-parser
|
||||
version: 3.0.8
|
||||
peerDependencies:
|
||||
react: 0.14 || 15 || 16 || 17 || 18
|
||||
dependencies:
|
||||
domhandler: registry.npmjs.org/domhandler/5.0.3
|
||||
html-dom-parser: registry.npmjs.org/html-dom-parser/3.1.3
|
||||
react: 18.2.0
|
||||
react-property: registry.npmjs.org/react-property/2.0.0
|
||||
style-to-js: registry.npmjs.org/style-to-js/1.1.3
|
||||
dev: false
|
||||
|
||||
registry.npmjs.org/htmlparser2/6.1.0:
|
||||
resolution: {integrity: sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==, registry: https://registry.yarnpkg.com/, tarball: https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz}
|
||||
name: htmlparser2
|
||||
|
@ -11424,42 +11354,3 @@ packages:
|
|||
domhandler: registry.npmjs.org/domhandler/4.3.1
|
||||
domutils: registry.npmjs.org/domutils/2.8.0
|
||||
entities: registry.npmjs.org/entities/2.2.0
|
||||
|
||||
registry.npmjs.org/htmlparser2/8.0.1:
|
||||
resolution: {integrity: sha512-4lVbmc1diZC7GUJQtRQ5yBAeUCL1exyMwmForWkRLnwyzWBFxN633SALPMGYaWZvKe9j1pRZJpauvmxENSp/EA==, registry: https://registry.yarnpkg.com/, tarball: https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.1.tgz}
|
||||
name: htmlparser2
|
||||
version: 8.0.1
|
||||
dependencies:
|
||||
domelementtype: registry.npmjs.org/domelementtype/2.3.0
|
||||
domhandler: registry.npmjs.org/domhandler/5.0.3
|
||||
domutils: registry.npmjs.org/domutils/3.0.1
|
||||
entities: registry.npmjs.org/entities/4.4.0
|
||||
dev: false
|
||||
|
||||
registry.npmjs.org/inline-style-parser/0.1.1:
|
||||
resolution: {integrity: sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q==, registry: https://registry.yarnpkg.com/, tarball: https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.1.1.tgz}
|
||||
name: inline-style-parser
|
||||
version: 0.1.1
|
||||
dev: false
|
||||
|
||||
registry.npmjs.org/react-property/2.0.0:
|
||||
resolution: {integrity: sha512-kzmNjIgU32mO4mmH5+iUyrqlpFQhF8K2k7eZ4fdLSOPFrD1XgEuSBv9LDEgxRXTMBqMd8ppT0x6TIzqE5pdGdw==, registry: https://registry.yarnpkg.com/, tarball: https://registry.npmjs.org/react-property/-/react-property-2.0.0.tgz}
|
||||
name: react-property
|
||||
version: 2.0.0
|
||||
dev: false
|
||||
|
||||
registry.npmjs.org/style-to-js/1.1.3:
|
||||
resolution: {integrity: sha512-zKI5gN/zb7LS/Vm0eUwjmjrXWw8IMtyA8aPBJZdYiQTXj4+wQ3IucOLIOnF7zCHxvW8UhIGh/uZh/t9zEHXNTQ==, registry: https://registry.yarnpkg.com/, tarball: https://registry.npmjs.org/style-to-js/-/style-to-js-1.1.3.tgz}
|
||||
name: style-to-js
|
||||
version: 1.1.3
|
||||
dependencies:
|
||||
style-to-object: registry.npmjs.org/style-to-object/0.4.1
|
||||
dev: false
|
||||
|
||||
registry.npmjs.org/style-to-object/0.4.1:
|
||||
resolution: {integrity: sha512-HFpbb5gr2ypci7Qw+IOhnP2zOU7e77b+rzM+wTzXzfi1PrtBCX0E7Pk4wL4iTLnhzZ+JgEGAhX81ebTg/aYjQw==, registry: https://registry.yarnpkg.com/, tarball: https://registry.npmjs.org/style-to-object/-/style-to-object-0.4.1.tgz}
|
||||
name: style-to-object
|
||||
version: 0.4.1
|
||||
dependencies:
|
||||
inline-style-parser: registry.npmjs.org/inline-style-parser/0.1.1
|
||||
dev: false
|
||||
|
|
|
@ -8,7 +8,6 @@ import {
|
|||
} from 'react';
|
||||
|
||||
import { markdownToHtml } from '@/services';
|
||||
import { htmlToReact } from '@/utils';
|
||||
|
||||
import { htmlRender } from './utils';
|
||||
|
||||
|
@ -51,9 +50,9 @@ const Index = ({ value }, ref) => {
|
|||
return (
|
||||
<div
|
||||
ref={previewRef}
|
||||
className="preview-wrap position-relative p-3 bg-light rounded text-break text-wrap mt-2 fmt">
|
||||
{htmlToReact(html)}
|
||||
</div>
|
||||
className="preview-wrap position-relative p-3 bg-light rounded text-break text-wrap mt-2 fmt"
|
||||
dangerouslySetInnerHTML={{ __html: html }}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -82,9 +82,11 @@ export function htmlRender(el: HTMLElement | null) {
|
|||
if (!el) return;
|
||||
// Replace all br tags with newlines
|
||||
// Fixed an issue where the BR tag in the editor block formula HTML caused rendering errors.
|
||||
el.querySelectorAll('br').forEach((br) => {
|
||||
br.parentNode?.insertBefore(document.createTextNode('\n'), br);
|
||||
br.parentNode?.removeChild(br);
|
||||
el.querySelectorAll('p').forEach((p) => {
|
||||
if (p.innerHTML.startsWith('$$') && p.innerHTML.endsWith('$$')) {
|
||||
const str = p.innerHTML.replace(/<br>/g, '\n');
|
||||
p.innerHTML = str;
|
||||
}
|
||||
});
|
||||
|
||||
import('mermaid').then(({ default: mermaid }) => {
|
||||
|
@ -106,6 +108,7 @@ export function htmlRender(el: HTMLElement | null) {
|
|||
render(el, {
|
||||
delimiters: [
|
||||
{ left: '$$', right: '$$', display: true },
|
||||
{ left: '$$<br>', right: '<br>$$', display: true },
|
||||
{
|
||||
left: '\\begin{equation}',
|
||||
right: '\\end{equation}',
|
||||
|
@ -121,8 +124,21 @@ export function htmlRender(el: HTMLElement | null) {
|
|||
},
|
||||
);
|
||||
|
||||
// remove change table style to htmlToReact function
|
||||
/**
|
||||
* @description: You modify the DOM with other scripts after React has rendered the DOM. This way, on the next render cycle (re-render), React cannot find the DOM node it rendered before, because it has been modified or removed by other scripts.
|
||||
*/
|
||||
// change table style
|
||||
|
||||
el.querySelectorAll('table').forEach((table) => {
|
||||
if (
|
||||
(table.parentNode as HTMLDivElement)?.classList.contains(
|
||||
'table-responsive',
|
||||
)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
table.classList.add('table', 'table-bordered');
|
||||
const div = document.createElement('div');
|
||||
div.className = 'table-responsive';
|
||||
table.parentNode?.replaceChild(div, table);
|
||||
div.appendChild(table);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import { FC } from 'react';
|
||||
import { FC, useEffect } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { usePageTags } from '@/hooks';
|
||||
import { useLegalPrivacy } from '@/services';
|
||||
import { htmlToReact } from '@/utils';
|
||||
import { htmlRender } from '@/components';
|
||||
|
||||
const Index: FC = () => {
|
||||
const { t } = useTranslation('translation', { keyPrefix: 'nav_menus' });
|
||||
|
@ -13,6 +13,15 @@ const Index: FC = () => {
|
|||
const { data: privacy } = useLegalPrivacy();
|
||||
const contentText = privacy?.privacy_policy_original_text;
|
||||
let matchUrl: URL | undefined;
|
||||
|
||||
useEffect(() => {
|
||||
const fmt = document.querySelector('.fmt') as HTMLElement;
|
||||
if (!fmt) {
|
||||
return;
|
||||
}
|
||||
htmlRender(fmt);
|
||||
}, [privacy?.privacy_policy_parsed_text]);
|
||||
|
||||
try {
|
||||
if (contentText) {
|
||||
matchUrl = new URL(contentText);
|
||||
|
@ -26,9 +35,12 @@ const Index: FC = () => {
|
|||
return (
|
||||
<>
|
||||
<h3 className="mb-4">{t('privacy')}</h3>
|
||||
<div className="fmt">
|
||||
{htmlToReact(privacy?.privacy_policy_parsed_text || '')}
|
||||
</div>
|
||||
<div
|
||||
className="fmt"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: privacy?.privacy_policy_parsed_text || '',
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import { FC } from 'react';
|
||||
import { FC, useEffect } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { usePageTags } from '@/hooks';
|
||||
import { useLegalTos } from '@/services';
|
||||
import { htmlToReact } from '@/utils';
|
||||
import { htmlRender } from '@/components';
|
||||
|
||||
const Index: FC = () => {
|
||||
const { t } = useTranslation('translation', { keyPrefix: 'nav_menus' });
|
||||
|
@ -13,6 +13,15 @@ const Index: FC = () => {
|
|||
const { data: tos } = useLegalTos();
|
||||
const contentText = tos?.terms_of_service_original_text;
|
||||
let matchUrl: URL | undefined;
|
||||
|
||||
useEffect(() => {
|
||||
const fmt = document.querySelector('.fmt') as HTMLElement;
|
||||
if (!fmt) {
|
||||
return;
|
||||
}
|
||||
htmlRender(fmt);
|
||||
}, [tos?.terms_of_service_parsed_text]);
|
||||
|
||||
try {
|
||||
if (contentText) {
|
||||
matchUrl = new URL(contentText);
|
||||
|
@ -23,12 +32,16 @@ const Index: FC = () => {
|
|||
window.location.replace(matchUrl.toString());
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h3 className="mb-4">{t('tos')}</h3>
|
||||
<div className="fmt">
|
||||
{htmlToReact(tos?.terms_of_service_parsed_text || '')}
|
||||
</div>
|
||||
<div
|
||||
className="fmt"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: tos?.terms_of_service_parsed_text || '',
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -12,7 +12,7 @@ import {
|
|||
FormatTime,
|
||||
htmlRender,
|
||||
} from '@/components';
|
||||
import { scrollToElementTop, bgFadeOut, htmlToReact } from '@/utils';
|
||||
import { scrollToElementTop, bgFadeOut } from '@/utils';
|
||||
import { AnswerItem } from '@/common/interface';
|
||||
import { acceptanceAnswer } from '@/services';
|
||||
|
||||
|
@ -84,9 +84,10 @@ const Index: FC<Props> = ({
|
|||
</Badge>
|
||||
</div>
|
||||
)}
|
||||
<article className="fmt text-break text-wrap">
|
||||
{htmlToReact(data?.html)}
|
||||
</article>
|
||||
<article
|
||||
className="fmt text-break text-wrap"
|
||||
dangerouslySetInnerHTML={{ __html: data?.html }}
|
||||
/>
|
||||
<div className="d-flex align-items-center mt-4">
|
||||
<Actions
|
||||
source="answer"
|
||||
|
|
|
@ -12,7 +12,7 @@ import {
|
|||
FormatTime,
|
||||
htmlRender,
|
||||
} from '@/components';
|
||||
import { formatCount, guard, htmlToReact } from '@/utils';
|
||||
import { formatCount, guard } from '@/utils';
|
||||
import { following } from '@/services';
|
||||
import { pathFactory } from '@/router/pathFactory';
|
||||
|
||||
|
@ -106,9 +106,11 @@ const Index: FC<Props> = ({ data, initPage, hasAnswer, isLogged }) => {
|
|||
return <Tag className="m-1" key={item.slug_name} data={item} />;
|
||||
})}
|
||||
</div>
|
||||
<article ref={ref} className="fmt text-break text-wrap mt-4">
|
||||
{htmlToReact(data?.html)}
|
||||
</article>
|
||||
<article
|
||||
ref={ref}
|
||||
className="fmt text-break text-wrap mt-4"
|
||||
dangerouslySetInnerHTML={{ __html: data?.html }}
|
||||
/>
|
||||
|
||||
<Actions
|
||||
className="mt-4"
|
||||
|
|
|
@ -6,10 +6,10 @@ import { useTranslation } from 'react-i18next';
|
|||
import dayjs from 'dayjs';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import { handleFormError, scrollToDocTop, htmlToReact } from '@/utils';
|
||||
import { handleFormError, scrollToDocTop } from '@/utils';
|
||||
import { usePageTags, usePromptWithUnload } from '@/hooks';
|
||||
import { pathFactory } from '@/router/pathFactory';
|
||||
import { Editor, EditorRef, Icon } from '@/components';
|
||||
import { Editor, EditorRef, Icon, htmlRender } from '@/components';
|
||||
import type * as Type from '@/common/interface';
|
||||
import {
|
||||
useQueryAnswerInfo,
|
||||
|
@ -73,6 +73,13 @@ const Index = () => {
|
|||
|
||||
const questionContentRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!questionContentRef?.current) {
|
||||
return;
|
||||
}
|
||||
htmlRender(questionContentRef.current);
|
||||
}, [questionContentRef]);
|
||||
|
||||
usePromptWithUnload({
|
||||
when: contentChanged,
|
||||
});
|
||||
|
@ -195,9 +202,9 @@ const Index = () => {
|
|||
<div className="question-content-wrap">
|
||||
<div
|
||||
ref={questionContentRef}
|
||||
className="content position-absolute top-0 w-100">
|
||||
{htmlToReact(data?.question.html)}
|
||||
</div>
|
||||
className="content position-absolute top-0 w-100"
|
||||
dangerouslySetInnerHTML={{ __html: data?.question.html }}
|
||||
/>
|
||||
<div
|
||||
className="resize-bottom"
|
||||
style={{ maxHeight: questionContentRef?.current?.scrollHeight }}
|
||||
|
|
|
@ -6,7 +6,7 @@ import { useTranslation } from 'react-i18next';
|
|||
import classNames from 'classnames';
|
||||
|
||||
import { usePageTags } from '@/hooks';
|
||||
import { Tag, TagSelector, FormatTime, Modal } from '@/components';
|
||||
import { Tag, TagSelector, FormatTime, Modal, htmlRender } from '@/components';
|
||||
import {
|
||||
useTagInfo,
|
||||
useQuerySynonymsTags,
|
||||
|
@ -16,7 +16,6 @@ import {
|
|||
} from '@/services';
|
||||
import { pathFactory } from '@/router/pathFactory';
|
||||
import { loggedUserInfoStore, toastStore } from '@/stores';
|
||||
import { htmlToReact } from '@/utils';
|
||||
|
||||
const TagIntroduction = () => {
|
||||
const userInfo = loggedUserInfoStore((state) => state.user);
|
||||
|
@ -45,6 +44,15 @@ const TagIntroduction = () => {
|
|||
});
|
||||
}
|
||||
}, [locationState]);
|
||||
|
||||
useEffect(() => {
|
||||
const fmt = document.querySelector('.content.fmt') as HTMLElement;
|
||||
if (!fmt) {
|
||||
return;
|
||||
}
|
||||
htmlRender(fmt);
|
||||
}, [tagInfo?.parsed_text]);
|
||||
|
||||
if (!tagInfo) {
|
||||
return null;
|
||||
}
|
||||
|
@ -145,9 +153,10 @@ const TagIntroduction = () => {
|
|||
/>
|
||||
</div>
|
||||
|
||||
<div className="content text-break">
|
||||
{htmlToReact(tagInfo?.parsed_text)}
|
||||
</div>
|
||||
<div
|
||||
className="content text-break fmt"
|
||||
dangerouslySetInnerHTML={{ __html: tagInfo?.parsed_text }}
|
||||
/>
|
||||
<div className="mt-4">
|
||||
{tagInfo?.member_actions.map((action, index) => {
|
||||
return (
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
import i18next from 'i18next';
|
||||
import parse from 'html-react-parser';
|
||||
import * as DOMPurify from 'dompurify';
|
||||
|
||||
const Diff = require('diff');
|
||||
|
||||
|
@ -235,32 +233,6 @@ function diffText(newText: string, oldText?: string): string {
|
|||
return result.join('');
|
||||
}
|
||||
|
||||
function htmlToReact(html: string) {
|
||||
const cleanedHtml = DOMPurify.sanitize(html, {
|
||||
USE_PROFILES: { html: true },
|
||||
});
|
||||
|
||||
const ele = document.createElement('div');
|
||||
ele.innerHTML = cleanedHtml;
|
||||
|
||||
ele.querySelectorAll('table').forEach((table) => {
|
||||
if (
|
||||
(!table || (table.parentNode as HTMLDivElement))?.classList.contains(
|
||||
'table-responsive',
|
||||
)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
table.classList.add('table', 'table-bordered');
|
||||
const div = document.createElement('div');
|
||||
div.className = 'table-responsive';
|
||||
table.parentNode?.replaceChild(div, table);
|
||||
div.appendChild(table);
|
||||
});
|
||||
return parse(ele.innerHTML);
|
||||
}
|
||||
|
||||
export {
|
||||
thousandthDivision,
|
||||
formatCount,
|
||||
|
@ -276,5 +248,4 @@ export {
|
|||
labelStyle,
|
||||
handleFormError,
|
||||
diffText,
|
||||
htmlToReact,
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue