import { cn } from '@tpmjs/utils/cn';
import { forwardRef, useEffect, useState } from 'react';
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
// eslint-disable-next-line import/no-internal-modules -- react-syntax-highlighter requires deep import for styles
import { prism, vscDarkPlus } from 'react-syntax-highlighter/dist/esm/styles/prism';
import { Icon } from '../Icon/Icon';
import type { CodeBlockProps } from './types';
import {
codeBlockCodeVariants,
codeBlockContainerVariants,
codeBlockCopyButtonVariants,
} from './variants';
/**
* Hook to detect if dark mode is active by checking for .dark class on html element
*/
function useDarkMode(): boolean {
const [isDark, setIsDark] = useState(false);
useEffect(() => {
// Check initial state
const checkDarkMode = () => {
setIsDark(document.documentElement.classList.contains('dark'));
};
checkDarkMode();
// Watch for class changes on html element
const observer = new MutationObserver(checkDarkMode);
observer.observe(document.documentElement, {
attributes: true,
attributeFilter: ['class'],
});
return () => observer.disconnect();
}, []);
return isDark;
}
/**
* CodeBlock component
*
* Displays formatted code with syntax highlighting and optional copy functionality.
* Uses Prism syntax highlighter for beautiful code display.
* Built with React and JSX.
*
* @example
* ```typescript
* import { CodeBlock } from '@tpmjs/ui/CodeBlock/CodeBlock';
*
* function MyComponent() {
* return (
*
* );
* }
* ```
*/
export const CodeBlock = forwardRef(
({ className, code, language = 'text', size = 'md', showCopy = true, theme, ...props }, ref) => {
const [copied, setCopied] = useState(false);
const isDarkMode = useDarkMode();
// Use provided theme or auto-detect from system
const effectiveTheme = theme ?? (isDarkMode ? 'dark' : 'light');
const handleCopy = async () => {
try {
await navigator.clipboard.writeText(code);
setCopied(true);
setTimeout(() => setCopied(false), 2000);
} catch (err) {
// Silently fail if clipboard API is not available
console.error('Failed to copy code:', err);
}
};
// Map language aliases to supported languages
const languageMap: Record = {
js: 'javascript',
ts: 'typescript',
jsx: 'jsx',
tsx: 'tsx',
py: 'python',
rb: 'ruby',
sh: 'bash',
yml: 'yaml',
};
const normalizedLanguage = languageMap[language] || language;
return (
);
}
);
CodeBlock.displayName = 'CodeBlock';