2
0

CodeBlock.tsx 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
  1. import { ReactNode } from 'react';
  2. import type { CodeComponent } from 'react-markdown/lib/ast-to-react';
  3. import { PrismAsyncLight } from 'react-syntax-highlighter';
  4. import { oneDark } from 'react-syntax-highlighter/dist/cjs/styles/prism';
  5. import styles from './CodeBlock.module.scss';
  6. function extractChildrenToIgnoreReactNode(children: ReactNode): ReactNode {
  7. if (children == null) {
  8. return children;
  9. }
  10. // Single element array
  11. if (Array.isArray(children) && children.length === 1) {
  12. return extractChildrenToIgnoreReactNode(children[0]);
  13. }
  14. // Multiple element array
  15. if (Array.isArray(children) && children.length > 1) {
  16. return children.map(node => extractChildrenToIgnoreReactNode(node)).join('');
  17. }
  18. // object
  19. if (typeof children === 'object') {
  20. const grandChildren = (children as any).children ?? (children as any).props.children;
  21. return extractChildrenToIgnoreReactNode(grandChildren);
  22. }
  23. return String(children).replace(/\n$/, '');
  24. }
  25. function CodeBlockSubstance({ lang, children }: { lang: string, children: ReactNode }): JSX.Element {
  26. // return alternative element
  27. // in order to fix "CodeBlock string is be [object Object] if searched"
  28. // see: https://github.com/weseek/growi/pull/7484
  29. //
  30. // Note: You can also remove this code if the user requests to see the code highlighted in Prism as-is.
  31. const isSimpleString = Array.isArray(children) && children.length === 1 && typeof children[0] === 'string';
  32. if (!isSimpleString) {
  33. return (
  34. <div className="code-highlighted" style={oneDark['pre[class*="language-"]']}>
  35. <code className={`language-${lang}`} style={oneDark['code[class*="language-"]']}>
  36. {children}
  37. </code>
  38. </div>
  39. );
  40. }
  41. return (
  42. <PrismAsyncLight
  43. className="code-highlighted"
  44. PreTag="div"
  45. style={oneDark}
  46. language={lang}
  47. >
  48. {extractChildrenToIgnoreReactNode(children)}
  49. </PrismAsyncLight>
  50. );
  51. }
  52. export const CodeBlock: CodeComponent = ({ inline, className, children }) => {
  53. if (inline) {
  54. return <code className={`code-inline ${className ?? ''}`}>{children}</code>;
  55. }
  56. // TODO: set border according to the value of 'customize:highlightJsStyleBorder'
  57. const match = /language-(\w+)(:?.+)?/.exec(className || '');
  58. const lang = match && match[1] ? match[1] : '';
  59. const name = match && match[2] ? match[2].slice(1) : null;
  60. return (
  61. <>
  62. {name != null && (
  63. <cite className={`code-highlighted-title ${styles['code-highlighted-title']}`}>{name}</cite>
  64. )}
  65. <CodeBlockSubstance lang={lang}>{children}</CodeBlockSubstance>
  66. </>
  67. );
  68. };