/*! Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. SPDX-License-Identifier: Apache-2.0 */ import { ENV_DEVELOPMENT, ENV_TEST } from '$config'; import { HelpPanel, LoadingIndicator } from 'aws-northstar'; import { HelpPanelProps } from 'aws-northstar/components/HelpPanel'; import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'; import { last } from 'lodash'; import { nanoid } from 'nanoid'; import React, { createContext, useCallback, useContext, useEffect, useState } from 'react'; import ReactMarkdown, { Options as ReactMarkdownOptions } from 'react-markdown'; import rehypeAddClasses from 'rehype-add-classes'; import rehypeRaw from 'rehype-raw'; import remarkGfm from 'remark-gfm'; import tomorrow from 'react-syntax-highlighter/dist/cjs/styles/hljs/tomorrow'; const generateId = () => nanoid(10); type Content = React.ReactNode; type ManagedHelpPanelDefinition = { id: string; content: Content }; type ManagedHelpPanelStack = ManagedHelpPanelDefinition[]; interface IHelpContext { managedPanels: ManagedHelpPanelStack; activePanel: ManagedHelpPanelDefinition | undefined; addHelpPanel: (id: string, content: Content) => void; removeHelpPanel: (id: string) => void; } const HelpContext = createContext({ managedPanels: [], activePanel: undefined, addHelpPanel: (id: string, content: Content) => { console.log('stub:addHelpPanel:', id, content); }, removeHelpPanel: (id: string) => { console.log('stub:removeHelpPanel:', id); }, }); export const HelpProvider: React.FC<{}> = ({ children }) => { const [panelStack, setPanelStack] = useState([]); const addHelpPanel = useCallback( (id: string, content: Content) => { setPanelStack((stack) => stack.concat({ id, content })); }, [setPanelStack], ); const removeHelpPanel = useCallback( (id: string) => { setPanelStack((stack) => stack.filter(({ id: _id }) => _id !== id)); }, [setPanelStack], ); const context: IHelpContext = { managedPanels: panelStack, activePanel: last(panelStack), addHelpPanel, removeHelpPanel, }; return {children}; }; function useHelpContext() { return useContext(HelpContext); } export const ManagedHelpPanelRenderer: React.FC<{}> = () => { const { activePanel } = useHelpContext(); if (activePanel == null) { return null; } return <>{activePanel.content}; }; export interface ManagedHelpPanelProps extends HelpPanelProps { children: string | Promise | React.ReactNode; } export const ManagedHelpPanel: React.FC = ({ children, ...props }) => { const [id] = useState(() => generateId()); const { addHelpPanel, removeHelpPanel } = useHelpContext(); useEffect(() => { if ( children != null && (typeof children === 'string' || (typeof children === 'object' && (children as any).then != null)) ) { addHelpPanel( id, {children as string | Promise} , ); } else { addHelpPanel(id, {children}); } return () => removeHelpPanel(id); }, [id]); // eslint-disable-line react-hooks/exhaustive-deps // This component never directly renders, it just adds the content to help stack which // shows the most contextual help (the last help) return null; }; export const MarkdownRenderer: React.FC<{ children: string | Promise }> = React.memo(({ children }) => { const [markdown, setMarkdown] = useState(); useEffect(() => { if (typeof children === 'string') { setMarkdown(children); } else { setMarkdown(undefined); (async () => { const resolved = await (children as any); const mdFile = resolved.default; if (ENV_TEST) { // For testing to support .md files we mock to string setMarkdown(mdFile); } else { const response = await fetch(mdFile); const responseText = await response.text(); setMarkdown(responseText); } })(); } }, [children]); if (markdown == null) { return ; } // support hot-reloading in development if (ENV_DEVELOPMENT) { return {markdown}; } return {markdown}; }); const PureMarkdownRenderer: React.FC<{ children: string }> = React.memo(({ children }) => { return {children}; }); const REACT_MARKDOWN_OPTIONS: Omit = { linkTarget: '_blank', remarkPlugins: [remarkGfm], rehypePlugins: [ rehypeRaw, [ rehypeAddClasses, { h1: 'MuiTypography-h1', h2: 'MuiTypography-h2', h3: 'MuiTypography-h3', h4: 'MuiTypography-h4', h5: 'MuiTypography-h5', h6: 'MuiTypography-h6', a: 'MuiTypography-colorPrimary', }, ], ], className: 'MuiTypography-root', components: { /* eslint-disable */ h1: ({ node: _node, ...props }) =>

, code({ node: _node, inline, className, children, ...props }) { const match = /language-(\w+)/.exec(className || ''); return !inline && match ? ( ) : ( {children} ); }, /* eslint-enable */ }, };