import { css } from '@emotion/css'; import DangerouslySetHtmlContent from 'dangerously-set-html-content'; import { useState } from 'react'; import { useDebounce } from 'react-use'; import { GrafanaTheme2, PanelProps, renderTextPanelMarkdown, textUtil, InterpolateFunction } from '@grafana/data'; import { CodeEditor, ScrollContainer, useStyles2 } from '@grafana/ui'; import config from 'app/core/config'; import { defaultCodeOptions, Options, TextMode } from './panelcfg.gen'; export interface Props extends PanelProps {} export function TextPanel(props: Props) { const styles = useStyles2(getStyles); const [processed, setProcessed] = useState({ mode: props.options.mode, content: processContent(props.options, props.replaceVariables, config.disableSanitizeHtml), }); useDebounce( () => { const { options, replaceVariables } = props; const content = processContent(options, replaceVariables, config.disableSanitizeHtml); if (content !== processed.content || options.mode !== processed.mode) { setProcessed({ mode: options.mode, content, }); } }, 100, [props] ); if (processed.mode === TextMode.Code) { const code = props.options.code ?? defaultCodeOptions; return ( ); } return (
); } function processContent(options: Options, interpolate: InterpolateFunction, disableSanitizeHtml: boolean): string { let { mode, content } = options; // Variables must be interpolated before content is converted to markdown so using variables // in URLs work properly content = interpolate(content, {}, options.code?.language === 'json' ? 'json' : 'html'); if (!content) { return ' '; } switch (mode) { case TextMode.Code: break; // nothing case TextMode.HTML: if (!disableSanitizeHtml) { content = textUtil.sanitizeTextPanelContent(content); } break; case TextMode.Markdown: default: // default to markdown content = renderTextPanelMarkdown(content, { noSanitize: disableSanitizeHtml, }); } return content; } const getStyles = (theme: GrafanaTheme2) => ({ codeEditorContainer: css({ '.monaco-editor .margin, .monaco-editor-background': { backgroundColor: theme.colors.background.primary, }, }), containStrict: css({ contain: 'strict', height: '100%', }), });