- Remove root biome.json (conflicted with packages/config/biome.json) - Format all files with correct config (spaces, not tabs) - Restore non-null assertions (ref!) in test files where refs are guaranteed - Biome's optional chaining conversion broke TypeScript inference in tests Fixes type-check and format-check CI failures. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
89 lines
1.9 KiB
TypeScript
89 lines
1.9 KiB
TypeScript
import { cn } from "@tpmjs/utils/cn";
|
|
import { forwardRef, useState } from "react";
|
|
import { Icon } from "../Icon/Icon";
|
|
import type { CodeBlockProps } from "./types";
|
|
import {
|
|
codeBlockCodeVariants,
|
|
codeBlockContainerVariants,
|
|
codeBlockCopyButtonVariants,
|
|
} from "./variants";
|
|
|
|
/**
|
|
* CodeBlock component
|
|
*
|
|
* Displays formatted code with optional copy functionality.
|
|
* Includes syntax-highlighted display and copy-to-clipboard button.
|
|
* Built with React and JSX.
|
|
*
|
|
* @example
|
|
* ```typescript
|
|
* import { CodeBlock } from '@tpmjs/ui/CodeBlock/CodeBlock';
|
|
*
|
|
* function MyComponent() {
|
|
* return (
|
|
* <CodeBlock
|
|
* code="npm install @tpmjs/registry"
|
|
* language="bash"
|
|
* size="md"
|
|
* showCopy={true}
|
|
* />
|
|
* );
|
|
* }
|
|
* ```
|
|
*/
|
|
export const CodeBlock = forwardRef<HTMLDivElement, CodeBlockProps>(
|
|
(
|
|
{
|
|
className,
|
|
code,
|
|
language = "text",
|
|
size = "md",
|
|
showCopy = true,
|
|
...props
|
|
},
|
|
ref,
|
|
) => {
|
|
const [copied, setCopied] = useState(false);
|
|
|
|
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);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div
|
|
ref={ref}
|
|
className={cn(codeBlockContainerVariants(), className)}
|
|
{...props}
|
|
>
|
|
<code
|
|
className={codeBlockCodeVariants({
|
|
size,
|
|
})}
|
|
data-language={language}
|
|
>
|
|
{code}
|
|
</code>
|
|
{showCopy && (
|
|
<button
|
|
type="button"
|
|
className={codeBlockCopyButtonVariants()}
|
|
onClick={handleCopy}
|
|
aria-label={copied ? "Copied!" : "Copy code"}
|
|
data-testid="copy-button"
|
|
>
|
|
<Icon icon={copied ? "check" : "copy"} size="sm" />
|
|
</button>
|
|
)}
|
|
</div>
|
|
);
|
|
},
|
|
);
|
|
|
|
CodeBlock.displayName = "CodeBlock";
|