Skip to content

Commit

Permalink
feat(graph): add copy button for entire target configuration
Browse files Browse the repository at this point in the history
  • Loading branch information
xiongemi committed Jun 14, 2024
1 parent e9b7439 commit 68b5f73
Show file tree
Hide file tree
Showing 12 changed files with 167 additions and 161 deletions.
47 changes: 12 additions & 35 deletions graph/ui-code-block/src/lib/json-code-block.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,8 @@
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 { JSX, ReactNode, useMemo } from 'react';
import { twMerge } from 'tailwind-merge';
import { CopyToClipboardButton } from '@nx/graph/ui-components';

export function JsonCodeBlockPreTag({
children,
Expand All @@ -30,45 +25,27 @@ export function JsonCodeBlockPreTag({
export interface JsonCodeBlockProps {
data: any;
renderSource: (propertyName: string) => ReactNode;
copyTooltipText: string;
}

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
<CopyToClipboardButton
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>
tooltipAlignment="right"
tooltipText={props.copyTooltipText}
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'
)}
/>
</div>
<SyntaxHighlighter
language="json"
Expand Down
1 change: 1 addition & 0 deletions graph/ui-components/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './lib/copy-to-clipboard-button';
export * from './lib/debounced-text-input';
export * from './lib/tag';
export * from './lib/dropdown';
Expand Down
20 changes: 20 additions & 0 deletions graph/ui-components/src/lib/copy-to-clipboard-button.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import type { Meta, StoryObj } from '@storybook/react';
import {
CopyToClipboardButton,
CopyToClipboardButtonProps,
} from './copy-to-clipboard-button';

const meta: Meta<typeof CopyToClipboardButton> = {
component: CopyToClipboardButton,
title: 'CopyToClipboardButton',
};
export default meta;

type Story = StoryObj<typeof CopyToClipboardButton>;

export const Simple: Story = {
args: {
text: 'Hello, world!',
tooltipAlignment: 'left',
} as CopyToClipboardButtonProps,
};
61 changes: 61 additions & 0 deletions graph/ui-components/src/lib/copy-to-clipboard-button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// @ts-ignore
import { CopyToClipboard } from 'react-copy-to-clipboard';
import { JSX, ReactNode, useEffect, useState } from 'react';
import {
ClipboardDocumentCheckIcon,
ClipboardDocumentIcon,
} from '@heroicons/react/24/outline';

export interface CopyToClipboardButtonProps {
text: string;
tooltipText?: string;
tooltipAlignment?: 'left' | 'right';
className?: string;
children?: ReactNode;
}

export function CopyToClipboardButton({
text,
tooltipAlignment,
tooltipText,
className,
children,
}: CopyToClipboardButtonProps) {
const [copied, setCopied] = useState(false);

useEffect(() => {
if (!copied) return;
const t = setTimeout(() => {
setCopied(false);
}, 3000);
return () => clearTimeout(t);
}, [copied]);

return (
<CopyToClipboard
text={text}
onCopy={() => {
setCopied(true);
}}
>
<button
type="button"
data-tooltip={tooltipText ? tooltipText : false}
data-tooltip-align-right={tooltipAlignment === 'right'}
data-tooltip-align-left={tooltipAlignment === 'left'}
className={className}
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
}}
>
{copied ? (
<ClipboardDocumentCheckIcon className="inline h-5 w-5 text-blue-500 dark:text-sky-500" />
) : (
<ClipboardDocumentIcon className="inline h-5 w-5" />
)}
{children}
</button>
</CopyToClipboard>
);
}

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { GraphError } from 'nx/src/command-line/graph/graph';
/* eslint-enable @nx/enforce-module-boundaries */
import { EyeIcon } from '@heroicons/react/24/outline';
import { PropertyInfoTooltip, Tooltip } from '@nx/graph/ui-tooltips';
import { CopyToClipboardButton } from '@nx/graph/ui-components';
import { TooltipTriggerText } from '../target-configuration-details/tooltip-trigger-text';
import { twMerge } from 'tailwind-merge';
import { Pill } from '../pill';
Expand Down Expand Up @@ -67,7 +68,7 @@ export const ProjectDetails = ({
>
<div
className={twMerge(
`flex items-center justify-between`,
`flex flex-wrap items-center justify-between`,
isCompact ? `gap-1` : `mb-4 gap-2`
)}
>
Expand All @@ -86,17 +87,23 @@ export const ProjectDetails = ({
className="h-6 w-6"
/>
</div>
<span>
{onViewInProjectGraph && viewInProjectGraphPosition === 'top' && (
{onViewInProjectGraph && viewInProjectGraphPosition === 'top' && (
<div className="flex flex-wrap gap-2">
<CopyToClipboardButton
text={JSON.stringify(project.data, null, 2)}
className="inline-flex cursor-pointer items-center gap-2 rounded-md px-2 py-1 text-base text-slate-600 ring-2 ring-inset ring-slate-400/40 hover:bg-slate-50 dark:text-slate-300 dark:ring-slate-400/30 dark:hover:bg-slate-800/60"
>
Copy Project
</CopyToClipboardButton>
<ViewInProjectGraphButton
callback={() =>
onViewInProjectGraph({ projectName: project.name })
}
/>
)}{' '}
</span>
</div>
)}
</div>
<div className="flex justify-between py-2">
<div className="flex flex-wrap justify-between py-2">
<div>
{projectData.metadata?.description ? (
<p className="mb-2 text-sm capitalize text-gray-500 dark:text-slate-400">
Expand Down Expand Up @@ -129,16 +136,22 @@ export const ProjectDetails = ({
) : null}
</div>
<div className="self-end">
<span>
{onViewInProjectGraph &&
viewInProjectGraphPosition === 'bottom' && (
{onViewInProjectGraph &&
viewInProjectGraphPosition === 'bottom' && (
<div className="flex flex-wrap gap-2">
<CopyToClipboardButton
text={JSON.stringify(project, null, 2)}
className="inline-flex cursor-pointer items-center gap-2 rounded-md px-2 py-1 text-base text-slate-600 ring-2 ring-inset ring-slate-400/40 hover:bg-slate-50 dark:text-slate-300 dark:ring-slate-400/30 dark:hover:bg-slate-800/60"
>
Copy Project
</CopyToClipboardButton>
<ViewInProjectGraphButton
callback={() =>
onViewInProjectGraph({ projectName: project.name })
}
/>
)}{' '}
</span>
</div>
)}
</div>
</div>
</header>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/* eslint-disable @nx/enforce-module-boundaries */
// nx-ignore-next-line
import type { TargetConfiguration } from '@nx/devkit';
import { CopyToClipboardButton } from '@nx/graph/ui-components';
import {
ChevronDownIcon,
ChevronUpIcon,
Expand All @@ -13,7 +14,6 @@ import { twMerge } from 'tailwind-merge';
import { Pill } from '../pill';
import { TargetTechnologies } from '../target-technologies/target-technologies';
import { SourceInfo } from '../source-info/source-info';
import { CopyToClipboard } from '../copy-to-clipboard/copy-to-clipboard';
import { getDisplayHeaderFromTargetConfiguration } from '../utils/get-display-header-from-target-configuration';
import { TargetExecutor } from '../target-executor/target-executor';

Expand Down Expand Up @@ -45,10 +45,6 @@ export const TargetConfigurationDetailsHeader = ({
onRunTarget,
onViewInTaskGraph,
}: TargetConfigurationDetailsHeaderProps) => {
const handleCopyClick = async (copyText: string) => {
await window.navigator.clipboard.writeText(copyText);
};

if (!collapsable) {
// when collapsable is false, isCollasped should be false
isCollasped = false;
Expand Down Expand Up @@ -123,6 +119,12 @@ export const TargetConfigurationDetailsHeader = ({
</div>
</div>
<div className="flex items-center gap-2">
<CopyToClipboardButton
text={JSON.stringify(targetConfiguration, null, 2)}
tooltipText={!isCollasped ? 'Copy Target' : undefined}
tooltipAlignment="right"
className="rounded-md bg-inherit p-1 text-sm text-slate-600 ring-1 ring-inset ring-slate-400/40 hover:bg-slate-200 dark:text-slate-300 dark:ring-slate-400/30 dark:hover:bg-slate-700/60"
/>
{onViewInTaskGraph && (
<button
className="rounded-md bg-inherit p-1 text-sm text-slate-600 ring-1 ring-inset ring-slate-400/40 hover:bg-slate-200 dark:text-slate-300 dark:ring-slate-400/30 dark:hover:bg-slate-700/60"
Expand Down Expand Up @@ -171,10 +173,9 @@ export const TargetConfigurationDetailsHeader = ({
nx run {projectName}:{targetName}
</code>
<span>
<CopyToClipboard
onCopy={() =>
handleCopyClick(`nx run ${projectName}:${targetName}`)
}
<CopyToClipboardButton
text={`nx run ${projectName}:${targetName}`}
tooltipText="Copy Command"
tooltipAlignment="right"
/>
</span>
Expand Down
Loading

0 comments on commit 68b5f73

Please sign in to comment.