You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tailchat/client/web/plugins/com.msgbyte.webview/src/group/GroupCustomWebPanelRender.tsx

119 lines
3.0 KiB
TypeScript

import React, { useEffect, useRef, useState } from 'react';
import { Translate } from '../translate';
import { FilterXSS, getDefaultWhiteList } from 'xss';
import { useWatch } from '@capital/common';
import { GroupExtraDataPanel, NoData, TextArea } from '@capital/component';
import styled from 'styled-components';
const EditModalContent = styled.div`
padding: 10px;
width: 80vw;
height: 80vh;
display: flex;
flex-direction: column;
overflow: hidden;
.main {
flex: 1;
overflow: hidden;
> textarea {
height: 100%;
resize: none;
}
}
`;
const xss = new FilterXSS({
css: false,
whiteList: { ...getDefaultWhiteList(), iframe: ['src', 'style', 'class'] },
onIgnoreTag: function (tag, html, options) {
if (['html', 'body', 'head', 'meta', 'style', 'div'].includes(tag)) {
// 不对其属性列表进行过滤
return html;
}
},
});
function getInjectedStyle() {
try {
// 当前面板文本颜色
const currentTextColor = document.defaultView.getComputedStyle(
document.querySelector('.tc-content-background')
).color;
return `<style>body { color: ${currentTextColor} }</style>`;
} catch (e) {
return '';
}
}
const GroupCustomWebPanelRender: React.FC<{ html: string }> = (props) => {
const ref = useRef<HTMLIFrameElement>(null);
const html = props.html;
useEffect(() => {
if (!ref.current || !html) {
return;
}
const doc = ref.current.contentWindow.document;
doc.open();
doc.writeln(getInjectedStyle(), xss.process(html));
doc.close();
}, [html]);
if (!html) {
return <NoData />;
}
return <iframe ref={ref} className="w-full h-full" />;
};
GroupCustomWebPanelRender.displayName = 'GroupCustomWebPanelRender';
const GroupCustomWebPanelEditor: React.FC<{
initValue: string;
onChange: (html: string) => void;
}> = React.memo((props) => {
const [html, setHtml] = useState(() => props.initValue ?? '');
useWatch([html], () => {
props.onChange(html);
});
return <TextArea value={html} onChange={(e) => setHtml(e.target.value)} />;
});
GroupCustomWebPanelEditor.displayName = 'GroupCustomWebPanelEditor';
const GroupCustomWebPanel: React.FC<{ panelInfo: any }> = (props) => {
return (
<GroupExtraDataPanel
names={['html']}
render={(dataMap: Record<string, string>) => {
return (
<GroupCustomWebPanelRender
html={dataMap['html'] ?? props.panelInfo?.meta?.html ?? ''}
/>
);
}}
renderEdit={(dataMap: Record<string, string>) => {
return (
<EditModalContent>
<div>{Translate.editTip}</div>
<div className="main">
<GroupCustomWebPanelEditor
initValue={dataMap['html'] ?? props.panelInfo?.meta?.html ?? ''}
onChange={(html) => (dataMap['html'] = html)}
/>
</div>
</EditModalContent>
);
}}
/>
);
};
GroupCustomWebPanel.displayName = 'GroupCustomWebPanel';
export default GroupCustomWebPanel;