From 276ea40ae7b33a3803534b7f94b8b477eb3e7ffb Mon Sep 17 00:00:00 2001
From: Ganesh <7030871503ganeshpatil@gmail.com>
Date: Sun, 23 Nov 2025 19:28:33 +0530
Subject: [PATCH] Fix: updated docs and added CodeBlock component
---
src/components/CodeBlock/CodeBlock.jsx | 148 ++++++++++++++++++++++++
src/components/CodeBlock/CodeBlock.scss | 98 ++++++++++++++++
src/mdx-components.js | 2 +
3 files changed, 248 insertions(+)
create mode 100644 src/components/CodeBlock/CodeBlock.jsx
create mode 100644 src/components/CodeBlock/CodeBlock.scss
diff --git a/src/components/CodeBlock/CodeBlock.jsx b/src/components/CodeBlock/CodeBlock.jsx
new file mode 100644
index 000000000000..1de4cb5b7357
--- /dev/null
+++ b/src/components/CodeBlock/CodeBlock.jsx
@@ -0,0 +1,148 @@
+import React, { useState, useRef, useEffect } from 'react';
+import PropTypes from 'prop-types';
+import './CodeBlock.scss';
+
+const CodeBlock = ({ children, ...props }) => {
+ const [copied, setCopied] = useState(false);
+ const preRef = useRef(null);
+ const timeoutRef = useRef(null);
+
+ // Extract the code content from the children
+ const getCodeContent = () => {
+ if (!preRef.current) return '';
+
+ const codeElement = preRef.current.querySelector('code');
+ if (codeElement) {
+ return codeElement.textContent || codeElement.innerText || '';
+ }
+
+ // Fallback: get text from pre element
+ return preRef.current.textContent || preRef.current.innerText || '';
+ };
+
+ const handleCopy = async () => {
+ const codeContent = getCodeContent();
+
+ if (!codeContent) return;
+
+ try {
+ await navigator.clipboard.writeText(codeContent);
+ setCopied(true);
+
+ // Clear any existing timeout
+ if (timeoutRef.current) {
+ clearTimeout(timeoutRef.current);
+ }
+
+ // Reset the copied state after 2 seconds
+ timeoutRef.current = setTimeout(() => {
+ setCopied(false);
+ }, 2000);
+ } catch (err) {
+ // Fallback for browsers that don't support clipboard API
+ console.error('Failed to copy code:', err);
+
+ // Try the fallback method
+ const textArea = document.createElement('textarea');
+ textArea.value = codeContent;
+ textArea.style.position = 'fixed';
+ textArea.style.left = '-999999px';
+ document.body.appendChild(textArea);
+ textArea.select();
+
+ try {
+ document.execCommand('copy');
+ setCopied(true);
+ if (timeoutRef.current) {
+ clearTimeout(timeoutRef.current);
+ }
+ timeoutRef.current = setTimeout(() => {
+ setCopied(false);
+ }, 2000);
+ } catch (fallbackErr) {
+ console.error('Fallback copy failed:', fallbackErr);
+ }
+
+ document.body.removeChild(textArea);
+ }
+ };
+
+ // Cleanup timeout on unmount
+ useEffect(() => {
+ return () => {
+ if (timeoutRef.current) {
+ clearTimeout(timeoutRef.current);
+ }
+ };
+ }, []);
+
+ return (
+
+
+ {children}
+
+
+
+ );
+};
+
+CodeBlock.propTypes = {
+ children: PropTypes.node.isRequired,
+};
+
+export default CodeBlock;
+
diff --git a/src/components/CodeBlock/CodeBlock.scss b/src/components/CodeBlock/CodeBlock.scss
new file mode 100644
index 000000000000..70273ad15619
--- /dev/null
+++ b/src/components/CodeBlock/CodeBlock.scss
@@ -0,0 +1,98 @@
+@import 'vars';
+@import 'functions';
+
+.code-block-wrapper {
+ position: relative;
+ margin: 1em 0; // Match the margin from .markdown pre
+
+ // Ensure pre has relative positioning for absolute button
+ pre {
+ position: relative;
+ margin: 0; // Remove margin from pre since wrapper has it
+ }
+}
+
+.code-block-copy-button {
+ position: absolute;
+ top: 8px;
+ right: 8px;
+ display: flex;
+ align-items: center;
+ gap: 6px;
+ padding: 6px 12px;
+ background-color: transparentize(getColor(elephant), 0.2);
+ border: 1px solid transparentize(getColor(white), 0.9);
+ border-radius: 4px;
+ color: getColor(malibu);
+ font-size: 12px;
+ font-family: $font-stack-body;
+ cursor: pointer;
+ transition: all 200ms ease;
+ z-index: 10;
+
+ // Subtle by default, more visible on hover
+ opacity: 0.7;
+
+ .code-block-wrapper:hover & {
+ opacity: 1;
+ background-color: transparentize(getColor(elephant), 0.1);
+ border-color: transparentize(getColor(white), 0.8);
+ }
+
+ &:hover {
+ background-color: transparentize(getColor(elephant), 0.05);
+ border-color: transparentize(getColor(white), 0.75);
+ color: lighten(getColor(malibu), 10%);
+ opacity: 1;
+ }
+
+ &:active {
+ transform: scale(0.98);
+ }
+
+ &:focus {
+ outline: 2px solid getColor(malibu);
+ outline-offset: 2px;
+ opacity: 1;
+ }
+}
+
+.code-block-copy-icon {
+ width: 16px;
+ height: 16px;
+ flex-shrink: 0;
+}
+
+.code-block-copy-text {
+ white-space: nowrap;
+ font-weight: 500;
+}
+
+// Dark theme support
+[data-theme='dark'] {
+ .code-block-copy-button {
+ background-color: transparentize(#131b1f, 0.3);
+ border-color: transparentize(getColor(white), 0.95);
+ color: #69a8ee;
+ opacity: 0.7;
+
+ .code-block-wrapper:hover & {
+ opacity: 1;
+ background-color: transparentize(#131b1f, 0.15);
+ border-color: transparentize(getColor(white), 0.9);
+ }
+
+ &:hover {
+ background-color: transparentize(#131b1f, 0.05);
+ border-color: transparentize(getColor(white), 0.8);
+ color: #82b7f6;
+ opacity: 1;
+ }
+
+ &:focus {
+ outline-color: #69a8ee;
+ opacity: 1;
+ }
+ }
+}
+
diff --git a/src/mdx-components.js b/src/mdx-components.js
index 0a27d381804e..e6ef9e8e6b16 100644
--- a/src/mdx-components.js
+++ b/src/mdx-components.js
@@ -1,6 +1,7 @@
import Badge from './components/Badge/Badge';
import LinkComponent from './components/mdxComponents/Link';
import StackBlitzPreview from './components/StackBlitzPreview/StackBlitzPreview';
+import CodeBlock from './components/CodeBlock/CodeBlock';
/** @returns {import('mdx/types.js').MDXComponents} */
export function useMDXComponents() {
@@ -8,5 +9,6 @@ export function useMDXComponents() {
a: LinkComponent,
Badge: Badge,
StackBlitzPreview: StackBlitzPreview,
+ pre: CodeBlock,
};
}