feat(graph): add copy button for entire target configuration (#26284)
<!-- 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="540" alt="Screenshot 2024-07-03 at 5 29 12 PM" src="https://github.com/nrwl/nx/assets/16211801/bed98c56-3bd5-4170-893b-cefe5fe292f9"> <img width="1195" alt="Screenshot 2024-07-03 at 5 29 03 PM" src="https://github.com/nrwl/nx/assets/16211801/544a4c4e-299f-40fc-a767-215d9c758dd8"> ## Related Issue(s) <!-- Please link the issue being fixed so it gets closed when this is merged. --> Fixes #
This commit is contained in:
parent
e09bad9363
commit
311710e56c
@ -1,13 +1,8 @@
|
|||||||
import {
|
|
||||||
ClipboardDocumentCheckIcon,
|
|
||||||
ClipboardDocumentIcon,
|
|
||||||
} from '@heroicons/react/24/outline';
|
|
||||||
// @ts-ignore
|
|
||||||
import { CopyToClipboard } from 'react-copy-to-clipboard';
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import SyntaxHighlighter, { createElement } from 'react-syntax-highlighter';
|
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 { twMerge } from 'tailwind-merge';
|
||||||
|
import { CopyToClipboardButton } from '@nx/graph/ui-components';
|
||||||
|
|
||||||
export function JsonCodeBlockPreTag({
|
export function JsonCodeBlockPreTag({
|
||||||
children,
|
children,
|
||||||
@ -30,45 +25,27 @@ export function JsonCodeBlockPreTag({
|
|||||||
export interface JsonCodeBlockProps {
|
export interface JsonCodeBlockProps {
|
||||||
data: any;
|
data: any;
|
||||||
renderSource: (propertyName: string) => ReactNode;
|
renderSource: (propertyName: string) => ReactNode;
|
||||||
|
copyTooltipText: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function JsonCodeBlock(props: JsonCodeBlockProps): JSX.Element {
|
export function JsonCodeBlock(props: JsonCodeBlockProps): JSX.Element {
|
||||||
const [copied, setCopied] = useState(false);
|
|
||||||
const jsonString = useMemo(
|
const jsonString = useMemo(
|
||||||
() => JSON.stringify(props.data, null, 2),
|
() => JSON.stringify(props.data, null, 2),
|
||||||
[props.data]
|
[props.data]
|
||||||
);
|
);
|
||||||
useEffect(() => {
|
|
||||||
if (!copied) return;
|
|
||||||
const t = setTimeout(() => {
|
|
||||||
setCopied(false);
|
|
||||||
}, 3000);
|
|
||||||
return () => clearTimeout(t);
|
|
||||||
}, [copied]);
|
|
||||||
return (
|
return (
|
||||||
<div className="code-block group relative w-full">
|
<div className="code-block group relative w-full">
|
||||||
<div className="absolute right-0 top-0 z-10 flex">
|
<div className="absolute right-0 top-0 z-10 flex">
|
||||||
<CopyToClipboard
|
<CopyToClipboardButton
|
||||||
text={jsonString}
|
text={jsonString}
|
||||||
onCopy={() => {
|
tooltipAlignment="right"
|
||||||
setCopied(true);
|
tooltipText={props.copyTooltipText}
|
||||||
}}
|
|
||||||
>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className={twMerge(
|
className={twMerge(
|
||||||
'not-prose flex',
|
'not-prose flex',
|
||||||
'border border-slate-200 bg-slate-50/50 p-2 dark:border-slate-700 dark:bg-slate-800/60',
|
'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'
|
'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>
|
</div>
|
||||||
<SyntaxHighlighter
|
<SyntaxHighlighter
|
||||||
language="json"
|
language="json"
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
export * from './lib/copy-to-clipboard-button';
|
||||||
export * from './lib/debounced-text-input';
|
export * from './lib/debounced-text-input';
|
||||||
export * from './lib/tag';
|
export * from './lib/tag';
|
||||||
export * from './lib/dropdown';
|
export * from './lib/dropdown';
|
||||||
|
|||||||
@ -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
graph/ui-components/src/lib/copy-to-clipboard-button.tsx
Normal file
61
graph/ui-components/src/lib/copy-to-clipboard-button.tsx
Normal file
@ -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>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -1,17 +0,0 @@
|
|||||||
import type { Meta, StoryObj } from '@storybook/react';
|
|
||||||
import { CopyToClipboard } from './copy-to-clipboard';
|
|
||||||
|
|
||||||
const meta: Meta<typeof CopyToClipboard> = {
|
|
||||||
component: CopyToClipboard,
|
|
||||||
title: 'CopyToClipboard',
|
|
||||||
};
|
|
||||||
export default meta;
|
|
||||||
|
|
||||||
type Story = StoryObj<typeof CopyToClipboard>;
|
|
||||||
|
|
||||||
export const Simple: Story = {
|
|
||||||
args: {
|
|
||||||
onCopy: () => {},
|
|
||||||
tooltipAlignment: 'left',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
@ -1,36 +0,0 @@
|
|||||||
import { ClipboardIcon } from '@heroicons/react/24/outline';
|
|
||||||
import { JSX, useEffect, useState } from 'react';
|
|
||||||
|
|
||||||
interface CopyToClipboardProps {
|
|
||||||
onCopy: () => void;
|
|
||||||
tooltipAlignment?: 'left' | 'right';
|
|
||||||
}
|
|
||||||
|
|
||||||
export function CopyToClipboard(props: CopyToClipboardProps): JSX.Element {
|
|
||||||
const [copied, setCopied] = useState(false);
|
|
||||||
useEffect(() => {
|
|
||||||
if (copied) {
|
|
||||||
const timeout = setTimeout(() => {
|
|
||||||
setCopied(false);
|
|
||||||
}, 3000);
|
|
||||||
return () => clearTimeout(timeout);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return (
|
|
||||||
<span
|
|
||||||
data-tooltip="Copy to clipboard"
|
|
||||||
data-tooltip-align-right={props.tooltipAlignment === 'right'}
|
|
||||||
>
|
|
||||||
<ClipboardIcon
|
|
||||||
className={`inline h-4 w-5 !cursor-pointer ${
|
|
||||||
copied ? 'text-sky-500' : ''
|
|
||||||
}`}
|
|
||||||
onClick={(e) => {
|
|
||||||
e.stopPropagation();
|
|
||||||
setCopied(true);
|
|
||||||
props.onCopy();
|
|
||||||
}}
|
|
||||||
></ClipboardIcon>
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ -1,5 +1,3 @@
|
|||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
||||||
|
|
||||||
/* eslint-disable @nx/enforce-module-boundaries */
|
/* eslint-disable @nx/enforce-module-boundaries */
|
||||||
// nx-ignore-next-line
|
// nx-ignore-next-line
|
||||||
import type { ProjectGraphProjectNode } from '@nx/devkit';
|
import type { ProjectGraphProjectNode } from '@nx/devkit';
|
||||||
@ -71,7 +69,7 @@ export const ProjectDetails = ({
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className={twMerge(
|
className={twMerge(
|
||||||
`flex items-center justify-between`,
|
`flex flex-wrap items-center justify-between`,
|
||||||
isCompact ? `gap-1` : `mb-4 gap-2`
|
isCompact ? `gap-1` : `mb-4 gap-2`
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
@ -90,17 +88,15 @@ export const ProjectDetails = ({
|
|||||||
className="h-6 w-6"
|
className="h-6 w-6"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<span>
|
|
||||||
{onViewInProjectGraph && viewInProjectGraphPosition === 'top' && (
|
{onViewInProjectGraph && viewInProjectGraphPosition === 'top' && (
|
||||||
<ViewInProjectGraphButton
|
<ViewInProjectGraphButton
|
||||||
callback={() =>
|
callback={() =>
|
||||||
onViewInProjectGraph({ projectName: project.name })
|
onViewInProjectGraph({ projectName: project.name })
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
)}{' '}
|
)}
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="flex justify-between py-2">
|
<div className="flex flex-wrap justify-between py-2">
|
||||||
<div>
|
<div>
|
||||||
{projectData.metadata?.description ? (
|
{projectData.metadata?.description ? (
|
||||||
<p className="mb-2 text-sm capitalize text-gray-500 dark:text-slate-400">
|
<p className="mb-2 text-sm capitalize text-gray-500 dark:text-slate-400">
|
||||||
@ -133,7 +129,6 @@ export const ProjectDetails = ({
|
|||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
<div className="self-end">
|
<div className="self-end">
|
||||||
<span>
|
|
||||||
{onViewInProjectGraph &&
|
{onViewInProjectGraph &&
|
||||||
viewInProjectGraphPosition === 'bottom' && (
|
viewInProjectGraphPosition === 'bottom' && (
|
||||||
<ViewInProjectGraphButton
|
<ViewInProjectGraphButton
|
||||||
@ -141,8 +136,7 @@ export const ProjectDetails = ({
|
|||||||
onViewInProjectGraph({ projectName: project.name })
|
onViewInProjectGraph({ projectName: project.name })
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
)}{' '}
|
)}
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
/* eslint-disable @nx/enforce-module-boundaries */
|
/* eslint-disable @nx/enforce-module-boundaries */
|
||||||
// nx-ignore-next-line
|
// nx-ignore-next-line
|
||||||
import type { TargetConfiguration } from '@nx/devkit';
|
import type { TargetConfiguration } from '@nx/devkit';
|
||||||
|
import { CopyToClipboardButton } from '@nx/graph/ui-components';
|
||||||
import {
|
import {
|
||||||
ChevronDownIcon,
|
ChevronDownIcon,
|
||||||
ChevronUpIcon,
|
ChevronUpIcon,
|
||||||
@ -17,7 +18,6 @@ import { twMerge } from 'tailwind-merge';
|
|||||||
import { Pill } from '../pill';
|
import { Pill } from '../pill';
|
||||||
import { TargetTechnologies } from '../target-technologies/target-technologies';
|
import { TargetTechnologies } from '../target-technologies/target-technologies';
|
||||||
import { SourceInfo } from '../source-info/source-info';
|
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 { getDisplayHeaderFromTargetConfiguration } from '../utils/get-display-header-from-target-configuration';
|
||||||
import { TargetExecutor } from '../target-executor/target-executor';
|
import { TargetExecutor } from '../target-executor/target-executor';
|
||||||
|
|
||||||
@ -53,10 +53,6 @@ export const TargetConfigurationDetailsHeader = ({
|
|||||||
onViewInTaskGraph,
|
onViewInTaskGraph,
|
||||||
onNxConnect,
|
onNxConnect,
|
||||||
}: TargetConfigurationDetailsHeaderProps) => {
|
}: TargetConfigurationDetailsHeaderProps) => {
|
||||||
const handleCopyClick = async (copyText: string) => {
|
|
||||||
await window.navigator.clipboard.writeText(copyText);
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!collapsable) {
|
if (!collapsable) {
|
||||||
// when collapsable is false, isCollasped should be false
|
// when collapsable is false, isCollasped should be false
|
||||||
isCollasped = false;
|
isCollasped = false;
|
||||||
@ -156,6 +152,12 @@ export const TargetConfigurationDetailsHeader = ({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2">
|
<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 && (
|
{onViewInTaskGraph && (
|
||||||
<button
|
<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"
|
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"
|
||||||
@ -194,21 +196,22 @@ export const TargetConfigurationDetailsHeader = ({
|
|||||||
</div>
|
</div>
|
||||||
{!isCollasped && (
|
{!isCollasped && (
|
||||||
<div className="ml-5 mt-2 text-sm">
|
<div className="ml-5 mt-2 text-sm">
|
||||||
|
<div className="flex">
|
||||||
<SourceInfo
|
<SourceInfo
|
||||||
data={sourceMap[`targets.${targetName}`]}
|
data={sourceMap[`targets.${targetName}`]}
|
||||||
propertyKey={`targets.${targetName}`}
|
propertyKey={`targets.${targetName}`}
|
||||||
color="text-gray-500 dark:text-slate-400"
|
color="text-gray-500 dark:text-slate-400"
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
{targetName !== 'nx-release-publish' && (
|
{targetName !== 'nx-release-publish' && (
|
||||||
<div className="mt-2 text-right">
|
<div className="mt-2 text-right">
|
||||||
<code className="ml-4 rounded bg-gray-100 px-2 py-1 font-mono text-gray-800 dark:bg-gray-700 dark:text-gray-300">
|
<code className="ml-4 rounded bg-gray-100 px-2 py-1 font-mono text-gray-800 dark:bg-gray-700 dark:text-gray-300">
|
||||||
nx run {projectName}:{targetName}
|
nx run {projectName}:{targetName}
|
||||||
</code>
|
</code>
|
||||||
<span>
|
<span>
|
||||||
<CopyToClipboard
|
<CopyToClipboardButton
|
||||||
onCopy={() =>
|
text={`nx run ${projectName}:${targetName}`}
|
||||||
handleCopyClick(`nx run ${projectName}:${targetName}`)
|
tooltipText="Copy Command"
|
||||||
}
|
|
||||||
tooltipAlignment="right"
|
tooltipAlignment="right"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@ -1,12 +1,11 @@
|
|||||||
/* eslint-disable @nx/enforce-module-boundaries */
|
/* eslint-disable @nx/enforce-module-boundaries */
|
||||||
// nx-ignore-next-line
|
// nx-ignore-next-line
|
||||||
import type { TargetConfiguration } from '@nx/devkit';
|
import type { TargetConfiguration } from '@nx/devkit';
|
||||||
|
|
||||||
import { JsonCodeBlock } from '@nx/graph/ui-code-block';
|
import { JsonCodeBlock } from '@nx/graph/ui-code-block';
|
||||||
|
import { CopyToClipboardButton } from '@nx/graph/ui-components';
|
||||||
import { useCallback, useContext, useEffect, useState } from 'react';
|
import { useCallback, useContext, useEffect, useState } from 'react';
|
||||||
import { FadingCollapsible } from './fading-collapsible';
|
import { FadingCollapsible } from './fading-collapsible';
|
||||||
import { TargetConfigurationProperty } from './target-configuration-property';
|
import { TargetConfigurationProperty } from './target-configuration-property';
|
||||||
import { CopyToClipboard } from '../copy-to-clipboard/copy-to-clipboard';
|
|
||||||
import { PropertyInfoTooltip, Tooltip } from '@nx/graph/ui-tooltips';
|
import { PropertyInfoTooltip, Tooltip } from '@nx/graph/ui-tooltips';
|
||||||
import { TooltipTriggerText } from './tooltip-trigger-text';
|
import { TooltipTriggerText } from './tooltip-trigger-text';
|
||||||
import { Pill } from '../pill';
|
import { Pill } from '../pill';
|
||||||
@ -53,10 +52,6 @@ export default function TargetConfigurationDetails({
|
|||||||
const [collapsed, setCollapsed] = useState(true);
|
const [collapsed, setCollapsed] = useState(true);
|
||||||
const { expandedTargets, toggleTarget } = useContext(ExpandedTargetsContext);
|
const { expandedTargets, toggleTarget } = useContext(ExpandedTargetsContext);
|
||||||
|
|
||||||
const handleCopyClick = async (copyText: string) => {
|
|
||||||
await window.navigator.clipboard.writeText(copyText);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleCollapseToggle = useCallback(() => {
|
const handleCollapseToggle = useCallback(() => {
|
||||||
if (toggleTarget) {
|
if (toggleTarget) {
|
||||||
toggleTarget(targetName);
|
toggleTarget(targetName);
|
||||||
@ -110,10 +105,7 @@ export default function TargetConfigurationDetails({
|
|||||||
<div className="p-4 text-base">
|
<div className="p-4 text-base">
|
||||||
<div className="group mb-4">
|
<div className="group mb-4">
|
||||||
<h4 className="mb-4">
|
<h4 className="mb-4">
|
||||||
<TargetExecutorTitle
|
<TargetExecutorTitle {...displayHeader} />
|
||||||
{...displayHeader}
|
|
||||||
handleCopyClick={handleCopyClick}
|
|
||||||
/>
|
|
||||||
</h4>
|
</h4>
|
||||||
<p className="pl-5 font-mono">
|
<p className="pl-5 font-mono">
|
||||||
<TargetExecutor {...displayHeader} link={link}>
|
<TargetExecutor {...displayHeader} link={link}>
|
||||||
@ -131,10 +123,7 @@ export default function TargetConfigurationDetails({
|
|||||||
{script && (
|
{script && (
|
||||||
<div className="group mb-4">
|
<div className="group mb-4">
|
||||||
<h4 className="mb-4">
|
<h4 className="mb-4">
|
||||||
<TargetExecutorTitle
|
<TargetExecutorTitle script={script} />
|
||||||
script={script}
|
|
||||||
handleCopyClick={handleCopyClick}
|
|
||||||
/>
|
|
||||||
</h4>
|
</h4>
|
||||||
<p className="pl-5 font-mono">
|
<p className="pl-5 font-mono">
|
||||||
<TargetExecutor script={script} link={link}>
|
<TargetExecutor script={script} link={link}>
|
||||||
@ -164,6 +153,7 @@ export default function TargetConfigurationDetails({
|
|||||||
<FadingCollapsible>
|
<FadingCollapsible>
|
||||||
<JsonCodeBlock
|
<JsonCodeBlock
|
||||||
data={options}
|
data={options}
|
||||||
|
copyTooltipText="Copy Options"
|
||||||
renderSource={(propertyName: string) => (
|
renderSource={(propertyName: string) => (
|
||||||
<TargetSourceInfo
|
<TargetSourceInfo
|
||||||
className="flex min-w-0 pl-4"
|
className="flex min-w-0 pl-4"
|
||||||
@ -198,14 +188,11 @@ export default function TargetConfigurationDetails({
|
|||||||
</span>
|
</span>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<span className="mb-1 ml-2 hidden group-hover:inline">
|
<span className="mb-1 ml-2 hidden group-hover:inline">
|
||||||
<CopyToClipboard
|
<CopyToClipboardButton
|
||||||
onCopy={() =>
|
text={`"inputs": ${JSON.stringify(
|
||||||
handleCopyClick(
|
|
||||||
`"inputs": ${JSON.stringify(
|
|
||||||
targetConfiguration.inputs
|
targetConfiguration.inputs
|
||||||
)}`
|
)}`}
|
||||||
)
|
tooltipText="Copy Inputs"
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
</h4>
|
</h4>
|
||||||
@ -239,14 +226,11 @@ export default function TargetConfigurationDetails({
|
|||||||
</span>
|
</span>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<span className="mb-1 ml-2 hidden group-hover:inline">
|
<span className="mb-1 ml-2 hidden group-hover:inline">
|
||||||
<CopyToClipboard
|
<CopyToClipboardButton
|
||||||
onCopy={() =>
|
text={`"outputs": ${JSON.stringify(
|
||||||
handleCopyClick(
|
|
||||||
`"outputs": ${JSON.stringify(
|
|
||||||
targetConfiguration.outputs
|
targetConfiguration.outputs
|
||||||
)}`
|
)}`}
|
||||||
)
|
tooltipText="Copy Outputs"
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
</h4>
|
</h4>
|
||||||
@ -279,15 +263,12 @@ export default function TargetConfigurationDetails({
|
|||||||
<TooltipTriggerText>Depends On</TooltipTriggerText>
|
<TooltipTriggerText>Depends On</TooltipTriggerText>
|
||||||
</span>
|
</span>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<span className="inline pl-4 opacity-0 transition-opacity duration-150 ease-in-out group-hover/line:opacity-100">
|
<span className="mb-1 ml-2 hidden group-hover:inline">
|
||||||
<CopyToClipboard
|
<CopyToClipboardButton
|
||||||
onCopy={() =>
|
text={`"dependsOn": ${JSON.stringify(
|
||||||
handleCopyClick(
|
|
||||||
`"dependsOn": ${JSON.stringify(
|
|
||||||
targetConfiguration.dependsOn
|
targetConfiguration.dependsOn
|
||||||
)}`
|
)}`}
|
||||||
)
|
tooltipText="Copy Depends On"
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
</h4>
|
</h4>
|
||||||
@ -336,6 +317,7 @@ export default function TargetConfigurationDetails({
|
|||||||
<FadingCollapsible>
|
<FadingCollapsible>
|
||||||
<JsonCodeBlock
|
<JsonCodeBlock
|
||||||
data={targetConfiguration.configurations}
|
data={targetConfiguration.configurations}
|
||||||
|
copyTooltipText="Copy Configurations"
|
||||||
renderSource={(propertyName: string) => (
|
renderSource={(propertyName: string) => (
|
||||||
<TargetSourceInfo
|
<TargetSourceInfo
|
||||||
className="flex min-w-0 pl-4"
|
className="flex min-w-0 pl-4"
|
||||||
|
|||||||
@ -1,29 +1,26 @@
|
|||||||
import { PropertyInfoTooltip, Tooltip } from '@nx/graph/ui-tooltips';
|
import { PropertyInfoTooltip, Tooltip } from '@nx/graph/ui-tooltips';
|
||||||
import { CopyToClipboard } from '../copy-to-clipboard/copy-to-clipboard';
|
import { CopyToClipboardButton } from '@nx/graph/ui-components';
|
||||||
import { TooltipTriggerText } from '../target-configuration-details/tooltip-trigger-text';
|
import { TooltipTriggerText } from '../target-configuration-details/tooltip-trigger-text';
|
||||||
|
|
||||||
export function TargetExecutorTitle({
|
export function TargetExecutorTitle({
|
||||||
commands,
|
commands,
|
||||||
command,
|
command,
|
||||||
script,
|
script,
|
||||||
handleCopyClick,
|
executor,
|
||||||
}: {
|
}: {
|
||||||
handleCopyClick: (copyText: string) => void;
|
|
||||||
commands?: string[];
|
commands?: string[];
|
||||||
command?: string;
|
command?: string;
|
||||||
script?: string;
|
script?: string;
|
||||||
|
executor?: string;
|
||||||
}) {
|
}) {
|
||||||
if (commands && commands.length) {
|
if (commands && commands.length) {
|
||||||
return (
|
return (
|
||||||
<span className="font-medium">
|
<span className="font-medium">
|
||||||
Commands
|
Commands
|
||||||
<span className="mb-1 ml-2 hidden group-hover:inline">
|
<span className="mb-1 ml-2 hidden group-hover:inline">
|
||||||
<CopyToClipboard
|
<CopyToClipboardButton
|
||||||
onCopy={() =>
|
text={`"commands": [${commands.map((c) => `"${c}"`).join(', ')}]`}
|
||||||
handleCopyClick(
|
tooltipText="Copy Commands"
|
||||||
`"commands": [${commands.map((c) => `"${c}"`).join(', ')}]`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
@ -34,8 +31,9 @@ export function TargetExecutorTitle({
|
|||||||
<span className="font-medium">
|
<span className="font-medium">
|
||||||
Command
|
Command
|
||||||
<span className="mb-1 ml-2 hidden group-hover:inline">
|
<span className="mb-1 ml-2 hidden group-hover:inline">
|
||||||
<CopyToClipboard
|
<CopyToClipboardButton
|
||||||
onCopy={() => handleCopyClick(`"command": "${command}"`)}
|
text={`"command": "${command}"`}
|
||||||
|
tooltipText="Copy Command"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
@ -46,7 +44,7 @@ export function TargetExecutorTitle({
|
|||||||
<span className="font-medium">
|
<span className="font-medium">
|
||||||
Script
|
Script
|
||||||
<span className="mb-1 ml-2 hidden group-hover:inline">
|
<span className="mb-1 ml-2 hidden group-hover:inline">
|
||||||
<CopyToClipboard onCopy={() => handleCopyClick(script)} />
|
<CopyToClipboardButton text={script} tooltipText="Copy Script" />
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
@ -58,6 +56,12 @@ export function TargetExecutorTitle({
|
|||||||
>
|
>
|
||||||
<span className="font-medium">
|
<span className="font-medium">
|
||||||
<TooltipTriggerText>Executor</TooltipTriggerText>
|
<TooltipTriggerText>Executor</TooltipTriggerText>
|
||||||
|
<span className="mb-1 ml-2 hidden group-hover:inline">
|
||||||
|
<CopyToClipboardButton
|
||||||
|
text={executor ?? ''}
|
||||||
|
tooltipText="Copy Executor"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
</span>
|
</span>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -338,7 +338,7 @@
|
|||||||
"npm-run-path": "^4.0.1",
|
"npm-run-path": "^4.0.1",
|
||||||
"preact": "10.6.4",
|
"preact": "10.6.4",
|
||||||
"react": "18.3.1",
|
"react": "18.3.1",
|
||||||
"react-copy-to-clipboard": "^5.0.3",
|
"react-copy-to-clipboard": "^5.1.0",
|
||||||
"react-dom": "18.3.1",
|
"react-dom": "18.3.1",
|
||||||
"react-syntax-highlighter": "^15.5.0",
|
"react-syntax-highlighter": "^15.5.0",
|
||||||
"regenerator-runtime": "0.13.7",
|
"regenerator-runtime": "0.13.7",
|
||||||
|
|||||||
2
pnpm-lock.yaml
generated
2
pnpm-lock.yaml
generated
@ -109,7 +109,7 @@ dependencies:
|
|||||||
specifier: 18.3.1
|
specifier: 18.3.1
|
||||||
version: 18.3.1
|
version: 18.3.1
|
||||||
react-copy-to-clipboard:
|
react-copy-to-clipboard:
|
||||||
specifier: ^5.0.3
|
specifier: ^5.1.0
|
||||||
version: 5.1.0(react@18.3.1)
|
version: 5.1.0(react@18.3.1)
|
||||||
react-dom:
|
react-dom:
|
||||||
specifier: 18.3.1
|
specifier: 18.3.1
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user