mirror of https://gitee.com/answerdev/answer.git
fix: use htmlRender control rich-text style and delet dompurify html-react-parser
This commit is contained in:
parent
9d4a06c1bf
commit
f41a2ac761
|
@ -22,9 +22,7 @@
|
||||||
"copy-to-clipboard": "^3.3.2",
|
"copy-to-clipboard": "^3.3.2",
|
||||||
"dayjs": "^1.11.5",
|
"dayjs": "^1.11.5",
|
||||||
"diff": "^5.1.0",
|
"diff": "^5.1.0",
|
||||||
"dompurify": "^2.4.3",
|
|
||||||
"emoji-regex": "^10.2.1",
|
"emoji-regex": "^10.2.1",
|
||||||
"html-react-parser": "^3.0.8",
|
|
||||||
"i18next": "^21.9.0",
|
"i18next": "^21.9.0",
|
||||||
"katex": "^0.16.2",
|
"katex": "^0.16.2",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
|
|
|
@ -29,7 +29,6 @@ specifiers:
|
||||||
customize-cra: ^1.0.0
|
customize-cra: ^1.0.0
|
||||||
dayjs: ^1.11.5
|
dayjs: ^1.11.5
|
||||||
diff: ^5.1.0
|
diff: ^5.1.0
|
||||||
dompurify: ^2.4.3
|
|
||||||
emoji-regex: ^10.2.1
|
emoji-regex: ^10.2.1
|
||||||
eslint: ^8.0.1
|
eslint: ^8.0.1
|
||||||
eslint-config-airbnb: ^19.0.4
|
eslint-config-airbnb: ^19.0.4
|
||||||
|
@ -43,7 +42,6 @@ specifiers:
|
||||||
eslint-plugin-promise: ^6.0.0
|
eslint-plugin-promise: ^6.0.0
|
||||||
eslint-plugin-react: ^7.30.1
|
eslint-plugin-react: ^7.30.1
|
||||||
eslint-plugin-react-hooks: ^4.6.0
|
eslint-plugin-react-hooks: ^4.6.0
|
||||||
html-react-parser: ^3.0.8
|
|
||||||
husky: ^8.0.1
|
husky: ^8.0.1
|
||||||
i18next: ^21.9.0
|
i18next: ^21.9.0
|
||||||
katex: ^0.16.2
|
katex: ^0.16.2
|
||||||
|
@ -83,9 +81,7 @@ dependencies:
|
||||||
copy-to-clipboard: 3.3.2
|
copy-to-clipboard: 3.3.2
|
||||||
dayjs: 1.11.5
|
dayjs: 1.11.5
|
||||||
diff: 5.1.0
|
diff: 5.1.0
|
||||||
dompurify: registry.npmjs.org/dompurify/2.4.3
|
|
||||||
emoji-regex: 10.2.1
|
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
|
i18next: 21.9.2
|
||||||
katex: 0.16.2
|
katex: 0.16.2
|
||||||
lodash: 4.17.21
|
lodash: 4.17.21
|
||||||
|
@ -11303,16 +11299,6 @@ packages:
|
||||||
domhandler: registry.npmjs.org/domhandler/4.3.1
|
domhandler: registry.npmjs.org/domhandler/4.3.1
|
||||||
entities: registry.npmjs.org/entities/2.2.0
|
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:
|
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}
|
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
|
name: domelementtype
|
||||||
|
@ -11331,27 +11317,12 @@ packages:
|
||||||
dependencies:
|
dependencies:
|
||||||
domelementtype: registry.npmjs.org/domelementtype/2.3.0
|
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:
|
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}
|
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
|
name: dompurify
|
||||||
version: 2.4.0
|
version: 2.4.0
|
||||||
dev: false
|
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:
|
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}
|
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
|
name: domutils
|
||||||
|
@ -11369,52 +11340,11 @@ packages:
|
||||||
domelementtype: registry.npmjs.org/domelementtype/2.3.0
|
domelementtype: registry.npmjs.org/domelementtype/2.3.0
|
||||||
domhandler: registry.npmjs.org/domhandler/4.3.1
|
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:
|
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}
|
resolution: {integrity: sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==, registry: https://registry.yarnpkg.com/, tarball: https://registry.npmjs.org/entities/-/entities-2.2.0.tgz}
|
||||||
name: entities
|
name: entities
|
||||||
version: 2.2.0
|
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:
|
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}
|
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
|
name: htmlparser2
|
||||||
|
@ -11424,42 +11354,3 @@ packages:
|
||||||
domhandler: registry.npmjs.org/domhandler/4.3.1
|
domhandler: registry.npmjs.org/domhandler/4.3.1
|
||||||
domutils: registry.npmjs.org/domutils/2.8.0
|
domutils: registry.npmjs.org/domutils/2.8.0
|
||||||
entities: registry.npmjs.org/entities/2.2.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';
|
} from 'react';
|
||||||
|
|
||||||
import { markdownToHtml } from '@/services';
|
import { markdownToHtml } from '@/services';
|
||||||
import { htmlToReact } from '@/utils';
|
|
||||||
|
|
||||||
import { htmlRender } from './utils';
|
import { htmlRender } from './utils';
|
||||||
|
|
||||||
|
@ -51,9 +50,9 @@ const Index = ({ value }, ref) => {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
ref={previewRef}
|
ref={previewRef}
|
||||||
className="preview-wrap position-relative p-3 bg-light rounded text-break text-wrap mt-2 fmt">
|
className="preview-wrap position-relative p-3 bg-light rounded text-break text-wrap mt-2 fmt"
|
||||||
{htmlToReact(html)}
|
dangerouslySetInnerHTML={{ __html: html }}
|
||||||
</div>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -82,9 +82,11 @@ export function htmlRender(el: HTMLElement | null) {
|
||||||
if (!el) return;
|
if (!el) return;
|
||||||
// Replace all br tags with newlines
|
// Replace all br tags with newlines
|
||||||
// Fixed an issue where the BR tag in the editor block formula HTML caused rendering errors.
|
// Fixed an issue where the BR tag in the editor block formula HTML caused rendering errors.
|
||||||
el.querySelectorAll('br').forEach((br) => {
|
el.querySelectorAll('p').forEach((p) => {
|
||||||
br.parentNode?.insertBefore(document.createTextNode('\n'), br);
|
if (p.innerHTML.startsWith('$$') && p.innerHTML.endsWith('$$')) {
|
||||||
br.parentNode?.removeChild(br);
|
const str = p.innerHTML.replace(/<br>/g, '\n');
|
||||||
|
p.innerHTML = str;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
import('mermaid').then(({ default: mermaid }) => {
|
import('mermaid').then(({ default: mermaid }) => {
|
||||||
|
@ -106,6 +108,7 @@ export function htmlRender(el: HTMLElement | null) {
|
||||||
render(el, {
|
render(el, {
|
||||||
delimiters: [
|
delimiters: [
|
||||||
{ left: '$$', right: '$$', display: true },
|
{ left: '$$', right: '$$', display: true },
|
||||||
|
{ left: '$$<br>', right: '<br>$$', display: true },
|
||||||
{
|
{
|
||||||
left: '\\begin{equation}',
|
left: '\\begin{equation}',
|
||||||
right: '\\end{equation}',
|
right: '\\end{equation}',
|
||||||
|
@ -121,8 +124,21 @@ export function htmlRender(el: HTMLElement | null) {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
// remove change table style to htmlToReact function
|
// change table style
|
||||||
/**
|
|
||||||
* @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.
|
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 { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
import { usePageTags } from '@/hooks';
|
import { usePageTags } from '@/hooks';
|
||||||
import { useLegalPrivacy } from '@/services';
|
import { useLegalPrivacy } from '@/services';
|
||||||
import { htmlToReact } from '@/utils';
|
import { htmlRender } from '@/components';
|
||||||
|
|
||||||
const Index: FC = () => {
|
const Index: FC = () => {
|
||||||
const { t } = useTranslation('translation', { keyPrefix: 'nav_menus' });
|
const { t } = useTranslation('translation', { keyPrefix: 'nav_menus' });
|
||||||
|
@ -13,6 +13,15 @@ const Index: FC = () => {
|
||||||
const { data: privacy } = useLegalPrivacy();
|
const { data: privacy } = useLegalPrivacy();
|
||||||
const contentText = privacy?.privacy_policy_original_text;
|
const contentText = privacy?.privacy_policy_original_text;
|
||||||
let matchUrl: URL | undefined;
|
let matchUrl: URL | undefined;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const fmt = document.querySelector('.fmt') as HTMLElement;
|
||||||
|
if (!fmt) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
htmlRender(fmt);
|
||||||
|
}, [privacy?.privacy_policy_parsed_text]);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (contentText) {
|
if (contentText) {
|
||||||
matchUrl = new URL(contentText);
|
matchUrl = new URL(contentText);
|
||||||
|
@ -26,9 +35,12 @@ const Index: FC = () => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<h3 className="mb-4">{t('privacy')}</h3>
|
<h3 className="mb-4">{t('privacy')}</h3>
|
||||||
<div className="fmt">
|
<div
|
||||||
{htmlToReact(privacy?.privacy_policy_parsed_text || '')}
|
className="fmt"
|
||||||
</div>
|
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 { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
import { usePageTags } from '@/hooks';
|
import { usePageTags } from '@/hooks';
|
||||||
import { useLegalTos } from '@/services';
|
import { useLegalTos } from '@/services';
|
||||||
import { htmlToReact } from '@/utils';
|
import { htmlRender } from '@/components';
|
||||||
|
|
||||||
const Index: FC = () => {
|
const Index: FC = () => {
|
||||||
const { t } = useTranslation('translation', { keyPrefix: 'nav_menus' });
|
const { t } = useTranslation('translation', { keyPrefix: 'nav_menus' });
|
||||||
|
@ -13,6 +13,15 @@ const Index: FC = () => {
|
||||||
const { data: tos } = useLegalTos();
|
const { data: tos } = useLegalTos();
|
||||||
const contentText = tos?.terms_of_service_original_text;
|
const contentText = tos?.terms_of_service_original_text;
|
||||||
let matchUrl: URL | undefined;
|
let matchUrl: URL | undefined;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const fmt = document.querySelector('.fmt') as HTMLElement;
|
||||||
|
if (!fmt) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
htmlRender(fmt);
|
||||||
|
}, [tos?.terms_of_service_parsed_text]);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (contentText) {
|
if (contentText) {
|
||||||
matchUrl = new URL(contentText);
|
matchUrl = new URL(contentText);
|
||||||
|
@ -23,12 +32,16 @@ const Index: FC = () => {
|
||||||
window.location.replace(matchUrl.toString());
|
window.location.replace(matchUrl.toString());
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<h3 className="mb-4">{t('tos')}</h3>
|
<h3 className="mb-4">{t('tos')}</h3>
|
||||||
<div className="fmt">
|
<div
|
||||||
{htmlToReact(tos?.terms_of_service_parsed_text || '')}
|
className="fmt"
|
||||||
</div>
|
dangerouslySetInnerHTML={{
|
||||||
|
__html: tos?.terms_of_service_parsed_text || '',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -12,7 +12,7 @@ import {
|
||||||
FormatTime,
|
FormatTime,
|
||||||
htmlRender,
|
htmlRender,
|
||||||
} from '@/components';
|
} from '@/components';
|
||||||
import { scrollToElementTop, bgFadeOut, htmlToReact } from '@/utils';
|
import { scrollToElementTop, bgFadeOut } from '@/utils';
|
||||||
import { AnswerItem } from '@/common/interface';
|
import { AnswerItem } from '@/common/interface';
|
||||||
import { acceptanceAnswer } from '@/services';
|
import { acceptanceAnswer } from '@/services';
|
||||||
|
|
||||||
|
@ -84,9 +84,10 @@ const Index: FC<Props> = ({
|
||||||
</Badge>
|
</Badge>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<article className="fmt text-break text-wrap">
|
<article
|
||||||
{htmlToReact(data?.html)}
|
className="fmt text-break text-wrap"
|
||||||
</article>
|
dangerouslySetInnerHTML={{ __html: data?.html }}
|
||||||
|
/>
|
||||||
<div className="d-flex align-items-center mt-4">
|
<div className="d-flex align-items-center mt-4">
|
||||||
<Actions
|
<Actions
|
||||||
source="answer"
|
source="answer"
|
||||||
|
|
|
@ -12,7 +12,7 @@ import {
|
||||||
FormatTime,
|
FormatTime,
|
||||||
htmlRender,
|
htmlRender,
|
||||||
} from '@/components';
|
} from '@/components';
|
||||||
import { formatCount, guard, htmlToReact } from '@/utils';
|
import { formatCount, guard } from '@/utils';
|
||||||
import { following } from '@/services';
|
import { following } from '@/services';
|
||||||
import { pathFactory } from '@/router/pathFactory';
|
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} />;
|
return <Tag className="m-1" key={item.slug_name} data={item} />;
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
<article ref={ref} className="fmt text-break text-wrap mt-4">
|
<article
|
||||||
{htmlToReact(data?.html)}
|
ref={ref}
|
||||||
</article>
|
className="fmt text-break text-wrap mt-4"
|
||||||
|
dangerouslySetInnerHTML={{ __html: data?.html }}
|
||||||
|
/>
|
||||||
|
|
||||||
<Actions
|
<Actions
|
||||||
className="mt-4"
|
className="mt-4"
|
||||||
|
|
|
@ -6,10 +6,10 @@ import { useTranslation } from 'react-i18next';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
|
||||||
import { handleFormError, scrollToDocTop, htmlToReact } from '@/utils';
|
import { handleFormError, scrollToDocTop } from '@/utils';
|
||||||
import { usePageTags, usePromptWithUnload } from '@/hooks';
|
import { usePageTags, usePromptWithUnload } from '@/hooks';
|
||||||
import { pathFactory } from '@/router/pathFactory';
|
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 type * as Type from '@/common/interface';
|
||||||
import {
|
import {
|
||||||
useQueryAnswerInfo,
|
useQueryAnswerInfo,
|
||||||
|
@ -73,6 +73,13 @@ const Index = () => {
|
||||||
|
|
||||||
const questionContentRef = useRef<HTMLDivElement>(null);
|
const questionContentRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!questionContentRef?.current) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
htmlRender(questionContentRef.current);
|
||||||
|
}, [questionContentRef]);
|
||||||
|
|
||||||
usePromptWithUnload({
|
usePromptWithUnload({
|
||||||
when: contentChanged,
|
when: contentChanged,
|
||||||
});
|
});
|
||||||
|
@ -195,9 +202,9 @@ const Index = () => {
|
||||||
<div className="question-content-wrap">
|
<div className="question-content-wrap">
|
||||||
<div
|
<div
|
||||||
ref={questionContentRef}
|
ref={questionContentRef}
|
||||||
className="content position-absolute top-0 w-100">
|
className="content position-absolute top-0 w-100"
|
||||||
{htmlToReact(data?.question.html)}
|
dangerouslySetInnerHTML={{ __html: data?.question.html }}
|
||||||
</div>
|
/>
|
||||||
<div
|
<div
|
||||||
className="resize-bottom"
|
className="resize-bottom"
|
||||||
style={{ maxHeight: questionContentRef?.current?.scrollHeight }}
|
style={{ maxHeight: questionContentRef?.current?.scrollHeight }}
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { useTranslation } from 'react-i18next';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
|
||||||
import { usePageTags } from '@/hooks';
|
import { usePageTags } from '@/hooks';
|
||||||
import { Tag, TagSelector, FormatTime, Modal } from '@/components';
|
import { Tag, TagSelector, FormatTime, Modal, htmlRender } from '@/components';
|
||||||
import {
|
import {
|
||||||
useTagInfo,
|
useTagInfo,
|
||||||
useQuerySynonymsTags,
|
useQuerySynonymsTags,
|
||||||
|
@ -16,7 +16,6 @@ import {
|
||||||
} from '@/services';
|
} from '@/services';
|
||||||
import { pathFactory } from '@/router/pathFactory';
|
import { pathFactory } from '@/router/pathFactory';
|
||||||
import { loggedUserInfoStore, toastStore } from '@/stores';
|
import { loggedUserInfoStore, toastStore } from '@/stores';
|
||||||
import { htmlToReact } from '@/utils';
|
|
||||||
|
|
||||||
const TagIntroduction = () => {
|
const TagIntroduction = () => {
|
||||||
const userInfo = loggedUserInfoStore((state) => state.user);
|
const userInfo = loggedUserInfoStore((state) => state.user);
|
||||||
|
@ -45,6 +44,15 @@ const TagIntroduction = () => {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [locationState]);
|
}, [locationState]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const fmt = document.querySelector('.content.fmt') as HTMLElement;
|
||||||
|
if (!fmt) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
htmlRender(fmt);
|
||||||
|
}, [tagInfo?.parsed_text]);
|
||||||
|
|
||||||
if (!tagInfo) {
|
if (!tagInfo) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -145,9 +153,10 @@ const TagIntroduction = () => {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="content text-break">
|
<div
|
||||||
{htmlToReact(tagInfo?.parsed_text)}
|
className="content text-break fmt"
|
||||||
</div>
|
dangerouslySetInnerHTML={{ __html: tagInfo?.parsed_text }}
|
||||||
|
/>
|
||||||
<div className="mt-4">
|
<div className="mt-4">
|
||||||
{tagInfo?.member_actions.map((action, index) => {
|
{tagInfo?.member_actions.map((action, index) => {
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
import i18next from 'i18next';
|
import i18next from 'i18next';
|
||||||
import parse from 'html-react-parser';
|
|
||||||
import * as DOMPurify from 'dompurify';
|
|
||||||
|
|
||||||
const Diff = require('diff');
|
const Diff = require('diff');
|
||||||
|
|
||||||
|
@ -235,32 +233,6 @@ function diffText(newText: string, oldText?: string): string {
|
||||||
return result.join('');
|
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 {
|
export {
|
||||||
thousandthDivision,
|
thousandthDivision,
|
||||||
formatCount,
|
formatCount,
|
||||||
|
@ -276,5 +248,4 @@ export {
|
||||||
labelStyle,
|
labelStyle,
|
||||||
handleFormError,
|
handleFormError,
|
||||||
diffText,
|
diffText,
|
||||||
htmlToReact,
|
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue