mirror of
https://github.com/laurent22/joplin.git
synced 2024-12-27 10:32:58 +02:00
168 lines
4.8 KiB
TypeScript
168 lines
4.8 KiB
TypeScript
import * as React from 'react';
|
|
import { useRef, useImperativeHandle, forwardRef, useEffect } from 'react';
|
|
import useViewIsReady from './hooks/useViewIsReady';
|
|
import useThemeCss from './hooks/useThemeCss';
|
|
import useContentSize from './hooks/useContentSize';
|
|
import useSubmitHandler from './hooks/useSubmitHandler';
|
|
import useHtmlLoader from './hooks/useHtmlLoader';
|
|
import useWebviewToPluginMessages from './hooks/useWebviewToPluginMessages';
|
|
import useScriptLoader from './hooks/useScriptLoader';
|
|
import Logger from '@joplin/utils/Logger';
|
|
import styled from 'styled-components';
|
|
import { focus } from '@joplin/lib/utils/focusHandler';
|
|
|
|
const logger = Logger.create('UserWebview');
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
|
type StyleProps = any;
|
|
|
|
export interface Props {
|
|
html: string;
|
|
scripts: string[];
|
|
pluginId: string;
|
|
viewId: string;
|
|
themeId: number;
|
|
minWidth?: number;
|
|
minHeight?: number;
|
|
fitToContent?: boolean;
|
|
borderBottom?: boolean;
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
|
theme?: any;
|
|
// eslint-disable-next-line @typescript-eslint/ban-types -- Old code before rule was applied
|
|
onSubmit?: Function;
|
|
// eslint-disable-next-line @typescript-eslint/ban-types -- Old code before rule was applied
|
|
onDismiss?: Function;
|
|
// eslint-disable-next-line @typescript-eslint/ban-types -- Old code before rule was applied
|
|
onReady?: Function;
|
|
}
|
|
|
|
const StyledFrame = styled.iframe<{ fitToContent: boolean; borderBottom: boolean }>`
|
|
padding: 0;
|
|
margin: 0;
|
|
width: ${(props: StyleProps) => props.fitToContent ? `${props.width}px` : '100%'};
|
|
height: ${(props: StyleProps) => props.fitToContent ? `${props.height}px` : '100%'};
|
|
border: none;
|
|
border-bottom: ${(props: StyleProps) => props.borderBottom ? `1px solid ${props.theme.dividerColor}` : 'none'};
|
|
`;
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
|
function serializeForm(form: any) {
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
|
const output: any = {};
|
|
const formData = new FormData(form);
|
|
for (const key of formData.keys()) {
|
|
output[key] = formData.get(key);
|
|
}
|
|
return output;
|
|
}
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
|
function serializeForms(document: any) {
|
|
const forms = document.getElementsByTagName('form');
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
|
const output: any = {};
|
|
let untitledIndex = 0;
|
|
|
|
for (const form of forms) {
|
|
const name = `${form.getAttribute('name')}` || (`form${untitledIndex++}`);
|
|
output[name] = serializeForm(form);
|
|
}
|
|
|
|
return output;
|
|
}
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
|
function UserWebview(props: Props, ref: any) {
|
|
const minWidth = props.minWidth ? props.minWidth : 200;
|
|
const minHeight = props.minHeight ? props.minHeight : 20;
|
|
|
|
const viewRef = useRef(null);
|
|
const isReady = useViewIsReady(viewRef);
|
|
const cssFilePath = useThemeCss({ pluginId: props.pluginId, themeId: props.themeId });
|
|
|
|
useEffect(() => {
|
|
if (isReady && props.onReady) props.onReady();
|
|
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
|
}, [isReady]);
|
|
|
|
function frameWindow() {
|
|
if (!viewRef.current) return null;
|
|
return viewRef.current.contentWindow;
|
|
}
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
|
function postMessage(name: string, args: any = null) {
|
|
const win = frameWindow();
|
|
if (!win) return;
|
|
|
|
logger.debug('Got message', name, args);
|
|
|
|
win.postMessage({ target: 'webview', name, args }, '*');
|
|
}
|
|
|
|
useImperativeHandle(ref, () => {
|
|
return {
|
|
formData: function() {
|
|
if (viewRef.current) {
|
|
return serializeForms(frameWindow().document);
|
|
} else {
|
|
return null;
|
|
}
|
|
},
|
|
focus: function() {
|
|
if (viewRef.current) focus('UserWebView::focus', viewRef.current);
|
|
},
|
|
};
|
|
});
|
|
|
|
const htmlHash = useHtmlLoader(
|
|
frameWindow(),
|
|
isReady,
|
|
postMessage,
|
|
props.html,
|
|
);
|
|
|
|
const contentSize = useContentSize(
|
|
frameWindow(),
|
|
htmlHash,
|
|
minWidth,
|
|
minHeight,
|
|
props.fitToContent,
|
|
isReady,
|
|
);
|
|
|
|
useSubmitHandler(
|
|
frameWindow(),
|
|
props.onSubmit,
|
|
props.onDismiss,
|
|
htmlHash,
|
|
);
|
|
|
|
useWebviewToPluginMessages(
|
|
frameWindow(),
|
|
isReady,
|
|
props.pluginId,
|
|
props.viewId,
|
|
postMessage,
|
|
);
|
|
|
|
useScriptLoader(
|
|
postMessage,
|
|
isReady,
|
|
props.scripts,
|
|
cssFilePath,
|
|
);
|
|
|
|
return <StyledFrame
|
|
id={props.viewId}
|
|
width={contentSize.width}
|
|
height={contentSize.height}
|
|
fitToContent={props.fitToContent}
|
|
ref={viewRef}
|
|
src="services/plugins/UserWebviewIndex.html"
|
|
borderBottom={props.borderBottom}
|
|
></StyledFrame>;
|
|
}
|
|
|
|
export default forwardRef(UserWebview);
|