CodeBlock.tsx 2.7 KB

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