CodeBlock.tsx 2.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
  1. 'use client';
  2. import React from 'react';
  3. import { useClipboard } from '@/hooks';
  4. import clsx from 'clsx';
  5. import { html as beautifyHtml } from 'js-beautify';
  6. import SyntaxHighlighter from 'react-syntax-highlighter';
  7. import Icon from '@/components/Icon';
  8. export default function CodeBlock({
  9. html,
  10. children,
  11. className,
  12. language = 'xml',
  13. copyable = true,
  14. beautify = false,
  15. hideLinks = false,
  16. }:{
  17. html?: string,
  18. children?: React.ReactNode,
  19. className?: string,
  20. language?: string,
  21. copyable?: boolean,
  22. beautify?: boolean,
  23. hideLinks?: boolean,
  24. }) {
  25. let code = html ?? (children || '').toString();
  26. const clipboard = useClipboard();
  27. if (hideLinks) {
  28. code = code
  29. .replace(/url\([^\)]+\)/g, 'url(...)')
  30. .replace(/img src="[^"]+"/g, 'img src="..."')
  31. .replace(
  32. /(\n?)(\s*)<svg[^>]+icon-tabler-([^"]+).*?<\/svg>/gms,
  33. '$1$2<!-- SVG icon from http://tabler-icons.io/i/$3 -->\n$2<svg>...</svg>'
  34. );
  35. }
  36. if (beautify) {
  37. code = beautifyHtml(code, {
  38. inline: 'strong',
  39. indent_size: 2,
  40. });
  41. }
  42. const languageNames = {
  43. js: 'JavaScript',
  44. ts: 'TypeScript',
  45. javascript: 'JavaScript',
  46. typescript: 'TypeScript',
  47. php: 'PHP',
  48. python: 'Python',
  49. ruby: 'Ruby',
  50. go: 'Go',
  51. };
  52. if (language === 'js') {
  53. language = 'javascript';
  54. } else if (language === 'html') {
  55. language = 'xml';
  56. }
  57. return (
  58. <div className="codeblock">
  59. {copyable && (
  60. <div className="codeblock-copy">
  61. <button
  62. className={clsx('btn btn-icon', clipboard.copied ? 'btn-green' : 'btn-dark')}
  63. onClick={() => clipboard.copy(html || '')}
  64. >
  65. {clipboard.copied ? <Icon name="check" /> : <Icon name="clipboard" />}
  66. </button>
  67. </div>
  68. )}
  69. <SyntaxHighlighter
  70. className={clsx(`language-${language}`, className)}
  71. language={language}
  72. useInlineStyles={false}
  73. >
  74. {code}
  75. </SyntaxHighlighter>
  76. </div>
  77. );
  78. }