From f2153bfb6046a09cbfcd24d84ae38368215a283b Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sun, 11 Aug 2024 09:49:35 +0000 Subject: [PATCH] Adds component for handling panel render --- .../src/components/plugins/PluginPanel.tsx | 93 +++++++++++++++++++ src/frontend/src/hooks/UsePluginPanels.tsx | 13 +-- 2 files changed, 96 insertions(+), 10 deletions(-) create mode 100644 src/frontend/src/components/plugins/PluginPanel.tsx diff --git a/src/frontend/src/components/plugins/PluginPanel.tsx b/src/frontend/src/components/plugins/PluginPanel.tsx new file mode 100644 index 0000000000..aef7959ed3 --- /dev/null +++ b/src/frontend/src/components/plugins/PluginPanel.tsx @@ -0,0 +1,93 @@ +import { t } from '@lingui/macro'; +import { Alert, Text } from '@mantine/core'; +import { AxiosInstance } from 'axios'; +import { useEffect, useRef } from 'react'; + +import { api } from '../../App'; +import { ModelType } from '../../enums/ModelType'; +import { PanelType } from '../nav/Panel'; + +interface PluginPanelProps extends PanelType { + src?: string; + params?: any; + targetModel?: ModelType | string; + targetId?: string | number | null; +} + +/* + * Definition of what we pass into a plugin panel + */ +interface PluginPanelParameters { + target: HTMLDivElement; + props: PluginPanelProps; + targetModel?: ModelType | string; + targetId?: number; + api: AxiosInstance; +} + +// Placeholder content for a panel with no content +function PanelNoContent() { + return ( + + {t`No content provided for this plugin`} + + ); +} + +/** + * TODO: Provide more context information to the plugin renderer: + * + * - api instance + * - custom context data from server + */ + +/** + * A custom panel which can be used to display plugin content. + * + * - Content is loaded dynamically (via the API) when a page is first loaded + * - Content can be provided from an external javascript module, or with raw HTML + * + * If content is provided from an external source, it is expected to define a function `render_panel` which will render the content. + * const render_panel = (element: HTMLElement, params: any) => {...} + * + * Where: + * - `element` is the HTML element to render the content into + * - `params` is the set of run-time parameters to pass to the content rendering function + */ +export default function PluginPanel({ props }: { props: PluginPanelProps }) { + const ref = useRef(); + + const loadExternalSource = async () => { + // Load content from external source + const src = await import(/* @vite-ignore */ props.src ?? ''); + + // We expect the external source to define a function which will render the content + if (src && src.render_panel && typeof src.render_panel === 'function') { + src.render_panel({ + target: ref.current, + props: props, + api: api, + targetModel: props.targetModel, + targetId: props.targetId + }); + } + }; + + useEffect(() => { + if (props.src) { + // Load content from external source + loadExternalSource(); + } else if (props.content) { + // If content is provided directly, render it into the panel + // ref.current.innerHTML = props.content; + } else { + // Something... went wrong? + } + }, [props]); + + if (!props.content && !props.src) { + return ; + } + + return
{props.content}
; +} diff --git a/src/frontend/src/hooks/UsePluginPanels.tsx b/src/frontend/src/hooks/UsePluginPanels.tsx index 52382bc995..b37d311e74 100644 --- a/src/frontend/src/hooks/UsePluginPanels.tsx +++ b/src/frontend/src/hooks/UsePluginPanels.tsx @@ -5,6 +5,7 @@ import { useMemo } from 'react'; import { api } from '../App'; import { PanelType } from '../components/nav/Panel'; +import PluginPanel from '../components/plugins/PluginPanel'; import { ApiEndpoints } from '../enums/ApiEndpoints'; import { ModelType } from '../enums/ModelType'; import { identifierString } from '../functions/conversion'; @@ -16,15 +17,6 @@ export type PluginPanelState = { panels: PanelType[]; }; -// Placeholder content for a panel with no content -function PanelNoContent() { - return ( - - {t`No content provided for this plugin`} - - ); -} - export function usePluginPanels({ targetModel, targetId @@ -39,6 +31,7 @@ export function usePluginPanels({ [globalSettings] ); + // API query to fetch information on available plugin panels const { isFetching, data } = useQuery({ enabled: pluginPanelsEnabled && !!targetModel, queryKey: [targetModel, targetId], @@ -70,7 +63,7 @@ export function usePluginPanels({ name: identifierString(`${pluginKey}-${panel.name}`), label: panel.label || t`Plugin Panel`, icon: , - content: panel.content || + content: }; }) ?? [] );