<!-- Please make sure you have read the submission guidelines before posting an PR --> <!-- https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#-submitting-a-pr --> <!-- Please make sure that your commit message follows our format --> <!-- Example: `fix(nx): must begin with lowercase` --> ## Current Behavior <!-- This is the behavior we have today --> ## Expected Behavior <!-- This is the behavior we should expect with the changes in this PR --> <img width="1155" alt="Screenshot 2024-05-29 at 5 47 20 PM" src="https://github.com/nrwl/nx/assets/16211801/025f08d5-52cf-4087-94a5-e3319c89f8b1"> <img width="1127" alt="Screenshot 2024-05-29 at 5 47 04 PM" src="https://github.com/nrwl/nx/assets/16211801/ff19514d-2513-4b13-ac9c-4b124ac0ce4a"> <img width="387" alt="Screenshot 2024-06-04 at 11 56 54 PM" src="https://github.com/nrwl/nx/assets/16211801/ea2f0c47-a444-4be8-9ccd-60fd2b534e12"> ## Related Issue(s) <!-- Please link the issue being fixed so it gets closed when this is merged. --> Fixes #
118 lines
3.3 KiB
TypeScript
118 lines
3.3 KiB
TypeScript
import {
|
|
ClipboardDocumentCheckIcon,
|
|
ClipboardDocumentIcon,
|
|
} from '@heroicons/react/24/outline';
|
|
// @ts-ignore
|
|
import { CopyToClipboard } from 'react-copy-to-clipboard';
|
|
// @ts-ignore
|
|
import SyntaxHighlighter, { createElement } from 'react-syntax-highlighter';
|
|
import { JSX, ReactNode, useEffect, useMemo, useState } from 'react';
|
|
import { twMerge } from 'tailwind-merge';
|
|
|
|
export function JsonCodeBlockPreTag({
|
|
children,
|
|
}: {
|
|
children: ReactNode;
|
|
}): JSX.Element {
|
|
return (
|
|
<div
|
|
className={twMerge(
|
|
'hljs not-prose w-full overflow-hidden rounded-md',
|
|
'font-mono text-sm',
|
|
'border border-slate-200 bg-slate-50/50 dark:border-slate-700 dark:bg-slate-800/60'
|
|
)}
|
|
>
|
|
<div className="p-4">{children}</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export interface JsonCodeBlockProps {
|
|
data: any;
|
|
renderSource: (propertyName: string) => ReactNode;
|
|
}
|
|
|
|
export function JsonCodeBlock(props: JsonCodeBlockProps): JSX.Element {
|
|
const [copied, setCopied] = useState(false);
|
|
const jsonString = useMemo(
|
|
() => JSON.stringify(props.data, null, 2),
|
|
[props.data]
|
|
);
|
|
useEffect(() => {
|
|
if (!copied) return;
|
|
const t = setTimeout(() => {
|
|
setCopied(false);
|
|
}, 3000);
|
|
return () => clearTimeout(t);
|
|
}, [copied]);
|
|
return (
|
|
<div className="code-block group relative w-full">
|
|
<div className="absolute right-0 top-0 z-10 flex">
|
|
<CopyToClipboard
|
|
text={jsonString}
|
|
onCopy={() => {
|
|
setCopied(true);
|
|
}}
|
|
>
|
|
<button
|
|
type="button"
|
|
className={twMerge(
|
|
'not-prose flex',
|
|
'border border-slate-200 bg-slate-50/50 p-2 dark:border-slate-700 dark:bg-slate-800/60',
|
|
'opacity-0 transition-opacity group-hover:opacity-100'
|
|
)}
|
|
>
|
|
{copied ? (
|
|
<ClipboardDocumentCheckIcon className="h-5 w-5 text-blue-500 dark:text-sky-500" />
|
|
) : (
|
|
<ClipboardDocumentIcon className="h-5 w-5" />
|
|
)}
|
|
</button>
|
|
</CopyToClipboard>
|
|
</div>
|
|
<SyntaxHighlighter
|
|
language="json"
|
|
children={jsonString}
|
|
PreTag={JsonCodeBlockPreTag}
|
|
renderer={sourcesRenderer(props.renderSource)}
|
|
/>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export function sourcesRenderer(
|
|
renderSource: (propertyName: string) => ReactNode
|
|
) {
|
|
return ({ rows, stylesheet }: any) => {
|
|
return rows.map((node: any, idx: number) => {
|
|
const element = createElement({
|
|
node,
|
|
stylesheet,
|
|
useInlineStyles: false,
|
|
key: `code-line-${idx}`,
|
|
});
|
|
let sourceElement: ReactNode;
|
|
const attrNode = node.children.find(
|
|
(c: any) =>
|
|
c.type === 'element' && c.properties?.className?.includes('hljs-attr')
|
|
);
|
|
if (attrNode?.children?.length) {
|
|
for (const child of attrNode.children) {
|
|
sourceElement = renderSource(child.value); // e.g. command
|
|
if (sourceElement) break;
|
|
}
|
|
}
|
|
return (
|
|
<span className="group/line flex" key={`code-group${idx}`}>
|
|
<span>{element}</span>
|
|
{sourceElement && (
|
|
<span className="min-w-0 flex-1 pl-2 opacity-0 transition-opacity duration-150 ease-in-out group-hover/line:opacity-100">
|
|
{sourceElement}
|
|
</span>
|
|
)}
|
|
</span>
|
|
);
|
|
});
|
|
};
|
|
}
|