cleanup(graph): misc project details view enhancements (#21311)
This commit is contained in:
parent
7ab0de8f68
commit
12d7caa099
@ -1,3 +1,7 @@
|
||||
/*! purgecss start ignore */
|
||||
@import './syntax-highlight.css';
|
||||
/*! purgecss end ignore */
|
||||
|
||||
@tailwind components;
|
||||
@tailwind base;
|
||||
@tailwind utilities;
|
||||
@ -6,3 +10,37 @@
|
||||
.dark {
|
||||
color-scheme: dark;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tooltips
|
||||
*/
|
||||
[data-tooltip] {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
[data-tooltip]:before {
|
||||
background-color: hsla(214, 61%, 11%, 1);
|
||||
border-radius: 0.375rem;
|
||||
color: hsla(0, 0%, 95%, 1);
|
||||
content: attr(data-tooltip);
|
||||
left: 0;
|
||||
opacity: 0;
|
||||
padding: 0.25rem 0.5rem;
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
text-transform: capitalize;
|
||||
top: 120%;
|
||||
transition: opacity 150ms ease-out;
|
||||
visibility: hidden;
|
||||
z-index: 1;
|
||||
width: max-content;
|
||||
}
|
||||
[data-tooltip][data-tooltip-align-right]:before {
|
||||
left: auto;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
[data-tooltip]:hover:before {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
79
graph/client/src/syntax-highlight.css
Normal file
79
graph/client/src/syntax-highlight.css
Normal file
@ -0,0 +1,79 @@
|
||||
html {
|
||||
--interactive-muted: #94a3b8;
|
||||
}
|
||||
.dark {
|
||||
--interactive-muted: #64748b;
|
||||
}
|
||||
|
||||
.hljs .hljs-comment,
|
||||
.hljs .hljs-quote {
|
||||
color: var(--interactive-muted);
|
||||
}
|
||||
|
||||
.hljs .hljs-addition,
|
||||
.hljs .hljs-keyword,
|
||||
.hljs .hljs-selector-tag {
|
||||
color: #859900;
|
||||
}
|
||||
|
||||
.hljs .hljs-doctag,
|
||||
.hljs .hljs-literal,
|
||||
.hljs .hljs-meta .hljs .hljs-meta-string,
|
||||
.hljs .hljs-number,
|
||||
.hljs .hljs-regexp,
|
||||
.hljs .hljs-string {
|
||||
color: #2aa198;
|
||||
}
|
||||
|
||||
.hljs .hljs-name,
|
||||
.hljs .hljs-section,
|
||||
.hljs .hljs-selector-class,
|
||||
.hljs .hljs-selector-id,
|
||||
.hljs .hljs-title {
|
||||
color: #268bd2;
|
||||
}
|
||||
|
||||
.hljs .hljs-attr,
|
||||
.hljs .hljs-attribute,
|
||||
.hljs .hljs-class .hljs .hljs-title,
|
||||
.hljs .hljs-template-variable,
|
||||
.hljs .hljs-type,
|
||||
.hljs .hljs-variable {
|
||||
color: #b58900;
|
||||
}
|
||||
|
||||
.hljs .hljs-bullet,
|
||||
.hljs .hljs-link,
|
||||
.hljs .hljs-meta,
|
||||
.hljs .hljs-meta .hljs .hljs-keyword,
|
||||
.hljs .hljs-selector-attr,
|
||||
.hljs .hljs-selector-pseudo,
|
||||
.hljs .hljs-subst,
|
||||
.hljs .hljs-symbol {
|
||||
color: #cb4b16;
|
||||
}
|
||||
|
||||
.hljs .hljs-built_in,
|
||||
.hljs .hljs-deletion {
|
||||
color: #dc322f;
|
||||
}
|
||||
|
||||
.hljs .hljs-formula {
|
||||
background: #073642;
|
||||
}
|
||||
|
||||
.hljs .hljs-emphasis {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.hljs .hljs-strong {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.linenumber {
|
||||
border-color: rgb(59 130 246);
|
||||
background: #e5e7eb;
|
||||
}
|
||||
html.dark .linenumber {
|
||||
background: #e5e7eb33;
|
||||
}
|
||||
@ -52,36 +52,25 @@ export function ProjectDetailsPage() {
|
||||
<div className="flex flex-col justify-center w-full text-slate-700 dark:text-slate-400">
|
||||
<ScrollRestoration />
|
||||
{environment !== 'nx-console' ? (
|
||||
<header className="flex w-full justify-center items-center px-4 py-2 border-b-2 border-slate-900/10 mb-8 dark:border-slate-300/10">
|
||||
<div className="flex flex-grow items-center justify-between max-w-6xl">
|
||||
<svg
|
||||
className="h-10 w-auto text-slate-900 dark:text-white"
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<title>Nx</title>
|
||||
<path d="M11.987 14.138l-3.132 4.923-5.193-8.427-.012 8.822H0V4.544h3.691l5.247 8.833.005-3.998 3.044 4.759zm.601-5.761c.024-.048 0-3.784.008-3.833h-3.65c.002.059-.005 3.776-.003 3.833h3.645zm5.634 4.134a2.061 2.061 0 0 0-1.969 1.336 1.963 1.963 0 0 1 2.343-.739c.396.161.917.422 1.33.283a2.1 2.1 0 0 0-1.704-.88zm3.39 1.061c-.375-.13-.8-.277-1.109-.681-.06-.08-.116-.17-.176-.265a2.143 2.143 0 0 0-.533-.642c-.294-.216-.68-.322-1.18-.322a2.482 2.482 0 0 0-2.294 1.536 2.325 2.325 0 0 1 4.002.388.75.75 0 0 0 .836.334c.493-.105.46.36 1.203.518v-.133c-.003-.446-.246-.55-.75-.733zm2.024 1.266a.723.723 0 0 0 .347-.638c-.01-2.957-2.41-5.487-5.37-5.487a5.364 5.364 0 0 0-4.487 2.418c-.01-.026-1.522-2.39-1.538-2.418H8.943l3.463 5.423-3.379 5.32h3.54l1.54-2.366 1.568 2.366h3.541l-3.21-5.052a.7.7 0 0 1-.084-.32 2.69 2.69 0 0 1 2.69-2.691h.001c1.488 0 1.736.89 2.057 1.308.634.826 1.9.464 1.9 1.541a.707.707 0 0 0 1.066.596zm.35.133c-.173.372-.56.338-.755.639-.176.271.114.412.114.412s.337.156.538-.311c.104-.231.14-.488.103-.74z" />
|
||||
</svg>
|
||||
<ul className="flex items-center">
|
||||
<li>
|
||||
<Link
|
||||
to={routeConstructor(
|
||||
`/projects/${encodeURIComponent(project.name)}`,
|
||||
true
|
||||
)}
|
||||
title="View in Graph"
|
||||
>
|
||||
Graph
|
||||
</Link>
|
||||
</li>
|
||||
<li className="ml-4">
|
||||
<ThemePanel />
|
||||
</li>
|
||||
</ul>
|
||||
<header className="flex w-full justify-center items-center py-2 mx-auto border-b-2 border-slate-900/10 mb-8 dark:border-slate-300/10">
|
||||
<div className="flex flex-grow items-center justify-between max-w-6xl px-8 ">
|
||||
<Link to={routeConstructor('/projects', false)}>
|
||||
<svg
|
||||
className="h-10 w-auto text-slate-900 dark:text-white"
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<title>Nx</title>
|
||||
<path d="M11.987 14.138l-3.132 4.923-5.193-8.427-.012 8.822H0V4.544h3.691l5.247 8.833.005-3.998 3.044 4.759zm.601-5.761c.024-.048 0-3.784.008-3.833h-3.65c.002.059-.005 3.776-.003 3.833h3.645zm5.634 4.134a2.061 2.061 0 0 0-1.969 1.336 1.963 1.963 0 0 1 2.343-.739c.396.161.917.422 1.33.283a2.1 2.1 0 0 0-1.704-.88zm3.39 1.061c-.375-.13-.8-.277-1.109-.681-.06-.08-.116-.17-.176-.265a2.143 2.143 0 0 0-.533-.642c-.294-.216-.68-.322-1.18-.322a2.482 2.482 0 0 0-2.294 1.536 2.325 2.325 0 0 1 4.002.388.75.75 0 0 0 .836.334c.493-.105.46.36 1.203.518v-.133c-.003-.446-.246-.55-.75-.733zm2.024 1.266a.723.723 0 0 0 .347-.638c-.01-2.957-2.41-5.487-5.37-5.487a5.364 5.364 0 0 0-4.487 2.418c-.01-.026-1.522-2.39-1.538-2.418H8.943l3.463 5.423-3.379 5.32h3.54l1.54-2.366 1.568 2.366h3.541l-3.21-5.052a.7.7 0 0 1-.084-.32 2.69 2.69 0 0 1 2.69-2.691h.001c1.488 0 1.736.89 2.057 1.308.634.826 1.9.464 1.9 1.541a.707.707 0 0 0 1.066.596zm.35.133c-.173.372-.56.338-.755.639-.176.271.114.412.114.412s.337.156.538-.311c.104-.231.14-.488.103-.74z" />
|
||||
</svg>
|
||||
</Link>
|
||||
<ThemePanel />
|
||||
</div>
|
||||
</header>
|
||||
) : null}
|
||||
) : (
|
||||
<div className="py-2"></div>
|
||||
)}
|
||||
<div className="flex-grow mx-auto w-full max-w-6xl px-8 mb-8">
|
||||
<ProjectDetailsWrapper
|
||||
project={project}
|
||||
|
||||
@ -17,7 +17,7 @@ export function JsonCodeBlockPreTag({
|
||||
return (
|
||||
<div
|
||||
className={twMerge(
|
||||
'hljs not-prose w-full overflow-hidden',
|
||||
'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'
|
||||
)}
|
||||
@ -103,10 +103,13 @@ export function sourcesRenderer(
|
||||
}
|
||||
}
|
||||
return (
|
||||
<span className="flex group/line" key={`code-group${idx}`}>
|
||||
<span
|
||||
className="flex group/line min-w-0 flex shrink-1"
|
||||
key={`code-group${idx}`}
|
||||
>
|
||||
<span>{element}</span>
|
||||
{sourceElement && (
|
||||
<span className="hidden group-hover/line:inline pl-2">
|
||||
<span className="opacity-0 min-w-0 flex shrink-1 group-hover/line:opacity-100 transition-opacity duration-150 ease-in-out inline pl-2">
|
||||
{sourceElement}
|
||||
</span>
|
||||
)}
|
||||
|
||||
28
graph/ui-project-details/src/lib/pill.tsx
Normal file
28
graph/ui-project-details/src/lib/pill.tsx
Normal file
@ -0,0 +1,28 @@
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
|
||||
export function Pill({
|
||||
text,
|
||||
color = 'grey',
|
||||
tooltip,
|
||||
}: {
|
||||
text: string;
|
||||
color?: 'grey' | 'green' | 'yellow';
|
||||
tooltip?: string;
|
||||
}) {
|
||||
return (
|
||||
<span
|
||||
data-tooltip={tooltip}
|
||||
className={twMerge(
|
||||
'inline-flex items-center rounded-md px-2 py-1 text-xs font-medium ring-1 ring-inset',
|
||||
color === 'grey' &&
|
||||
'bg-slate-400/10 text-slate-600 ring-slate-400/40 dark:text-slate-400 dark:ring-slate-400/30',
|
||||
color === 'green' &&
|
||||
'dark:bg-green-500/10 bg-green-400/10 dark:text-green-400 text-green-500 dark:ring-green-500/20 ring-green-500/40',
|
||||
color === 'yellow' &&
|
||||
'bg-yellow-50 dark:bg-yellow-900/30 text-yellow-600 dark:text-yellow-400 dark:ring-yellow-500/20 ring-yellow-500/40'
|
||||
)}
|
||||
>
|
||||
{text}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
@ -20,6 +20,7 @@ import {
|
||||
useRef,
|
||||
} from 'react';
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
import { Pill } from '../pill';
|
||||
|
||||
export interface ProjectDetailsProps {
|
||||
project: ProjectGraphProjectNode;
|
||||
@ -90,34 +91,42 @@ export const ProjectDetails = forwardRef(
|
||||
>
|
||||
<h1
|
||||
className={twMerge(
|
||||
`flex items-center`,
|
||||
`flex items-center justify-between dark:text-slate-100`,
|
||||
isCompact ? `text-2xl gap-1` : `text-4xl mb-4 gap-2`
|
||||
)}
|
||||
>
|
||||
{name}{' '}
|
||||
{onViewInProjectGraph ? (
|
||||
<EyeIcon
|
||||
className="h-5 w-5 cursor-pointer"
|
||||
onClick={() => onViewInProjectGraph({ projectName: name })}
|
||||
></EyeIcon>
|
||||
) : null}{' '}
|
||||
<span>{name}</span>
|
||||
<span>
|
||||
{onViewInProjectGraph ? (
|
||||
<button
|
||||
className="text-base cursor-pointer items-center inline-flex gap-2 text-slate-600 dark:text-slate-300 ring-2 ring-inset ring-slate-400/40 dark:ring-slate-400/30 hover:bg-slate-50 dark:hover:bg-slate-800/60 rounded-md py-1 px-2"
|
||||
onClick={() => onViewInProjectGraph({ projectName: name })}
|
||||
>
|
||||
<EyeIcon className="h-5 w-5 "></EyeIcon>
|
||||
<span>View In Graph</span>
|
||||
</button>
|
||||
) : null}{' '}
|
||||
</span>
|
||||
</h1>
|
||||
<div className={isCompact ? `px-4 py-2` : `p-4`}>
|
||||
{projectData.tags ? (
|
||||
<div className="py-2 ">
|
||||
{projectData.tags && projectData.tags.length ? (
|
||||
<p>
|
||||
<span className="font-medium inline-block w-10">Tags:</span>
|
||||
{projectData.tags?.map((tag) => (
|
||||
<span className="bg-slate-300 rounded-md p-1 mr-2">
|
||||
{tag}
|
||||
<span className="ml-2 font-mono">
|
||||
<Pill text={tag} />
|
||||
</span>
|
||||
))}
|
||||
</p>
|
||||
) : null}
|
||||
<p>
|
||||
<span className="font-bold">Root:</span> {root}
|
||||
<span className="font-medium inline-block w-10">Root:</span>
|
||||
<span className="font-mono"> {root}</span>
|
||||
</p>
|
||||
{displayType ? (
|
||||
<p>
|
||||
<span className="font-bold">Type:</span> {displayType}
|
||||
<span className="font-medium inline-block w-10">Type:</span>
|
||||
<span className="font-mono"> {displayType}</span>
|
||||
</p>
|
||||
) : null}
|
||||
</div>
|
||||
@ -128,13 +137,13 @@ export const ProjectDetails = forwardRef(
|
||||
openAction="hover"
|
||||
content={(<PropertyInfoTooltip type="targets" />) as any}
|
||||
>
|
||||
<span>
|
||||
<span className="text-slate-800 dark:text-slate-200">
|
||||
<TooltipTriggerText>Targets</TooltipTriggerText>
|
||||
</span>
|
||||
</Tooltip>
|
||||
</h2>
|
||||
<ul>
|
||||
{projectTargets.map((targetName) => {
|
||||
{projectTargets.sort(sortNxReleasePublishLast).map((targetName) => {
|
||||
const target = projectData.targets?.[targetName];
|
||||
return target && targetRefs.current[targetName] ? (
|
||||
<li className="mb-4 last:mb-0" key={`target-${targetName}`}>
|
||||
@ -160,4 +169,10 @@ export const ProjectDetails = forwardRef(
|
||||
}
|
||||
);
|
||||
|
||||
function sortNxReleasePublishLast(a: string, b: string) {
|
||||
if (a === 'nx-release-publish') return 1;
|
||||
if (b === 'nx-release-publish') return -1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
export default ProjectDetails;
|
||||
|
||||
@ -3,6 +3,7 @@ import { JSX, useEffect, useState } from 'react';
|
||||
|
||||
interface CopyToClipboardProps {
|
||||
onCopy: () => void;
|
||||
tooltipAlignment?: 'left' | 'right';
|
||||
}
|
||||
|
||||
export function CopyToClipboard(props: CopyToClipboardProps): JSX.Element {
|
||||
@ -16,16 +17,20 @@ export function CopyToClipboard(props: CopyToClipboardProps): JSX.Element {
|
||||
}
|
||||
});
|
||||
return (
|
||||
<ClipboardIcon
|
||||
title="Copy to clipboard"
|
||||
className={`inline h-4 w-5 cursor-pointer ${
|
||||
copied ? 'text-sky-500' : ''
|
||||
}`}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
setCopied(true);
|
||||
props.onCopy();
|
||||
}}
|
||||
></ClipboardIcon>
|
||||
<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>
|
||||
);
|
||||
}
|
||||
|
||||
@ -33,7 +33,9 @@ export function FadingCollapsible({ children }: { children: ReactNode }) {
|
||||
>
|
||||
<div
|
||||
className={`${
|
||||
collapsed && isCollapsible ? 'hover:bg-slate-700' : ''
|
||||
collapsed && isCollapsible
|
||||
? 'hover:bg-slate-200 dark:hover:bg-slate-700'
|
||||
: ''
|
||||
} rounded-md`}
|
||||
style={fadeStyles}
|
||||
>
|
||||
|
||||
@ -1,15 +1,17 @@
|
||||
import { SourcemapInfoToolTip, Tooltip } from '@nx/graph/ui-tooltips';
|
||||
import { InformationCircleIcon } from '@heroicons/react/24/outline';
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
|
||||
export function SourceInfo(props: {
|
||||
data: Array<string>;
|
||||
propertyKey: string;
|
||||
color?: string;
|
||||
}) {
|
||||
// Target property key is in the form `target.${targetName}`
|
||||
// Every other property within in the target has the form `target.${targetName}.${propertyName}
|
||||
const isTarget = props.propertyKey.split('.').length === 2;
|
||||
return (
|
||||
<span className="inline-flex items-center gap-2">
|
||||
<span className="inline-flex shrink-1 min-w-0 items-center gap-2">
|
||||
<Tooltip
|
||||
openAction="hover"
|
||||
strategy="fixed"
|
||||
@ -28,7 +30,12 @@ export function SourceInfo(props: {
|
||||
{/*<span className="italic text-gray-500">*/}
|
||||
{/* <InformationCircleIcon className="w-3 h-3" />*/}
|
||||
{/*</span>*/}
|
||||
<span className="italic text-gray-500">
|
||||
<span
|
||||
className={twMerge(
|
||||
'italic text-sm min-w-0 truncate',
|
||||
props.color ?? 'text-gray-500'
|
||||
)}
|
||||
>
|
||||
{isTarget ? 'Created' : 'Set'} by {props.data?.[1]} from{' '}
|
||||
{props.data?.[0]}
|
||||
</span>
|
||||
|
||||
@ -4,7 +4,9 @@ import {
|
||||
ChevronDownIcon,
|
||||
ChevronUpIcon,
|
||||
EyeIcon,
|
||||
InformationCircleIcon,
|
||||
PlayIcon,
|
||||
QuestionMarkCircleIcon,
|
||||
} from '@heroicons/react/24/outline';
|
||||
|
||||
// nx-ignore-next-line
|
||||
@ -31,6 +33,7 @@ import {
|
||||
} from '@nx/graph/ui-tooltips';
|
||||
import { TooltipTriggerText } from './tooltip-trigger-text';
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
import { Pill } from '../pill';
|
||||
|
||||
/* eslint-disable-next-line */
|
||||
export interface TargetProps {
|
||||
@ -140,40 +143,43 @@ export const TargetConfigurationDetails = forwardRef(
|
||||
: true);
|
||||
|
||||
return (
|
||||
<div className="rounded-md border border-slate-500 relative overflow-hidden">
|
||||
<div className="rounded-md border border-slate-200 dark:border-slate-700/60 relative overflow-hidden">
|
||||
<header
|
||||
className={twMerge(
|
||||
`group hover:bg-slate-200 dark:hover:bg-slate-800 cursor-pointer`,
|
||||
`group hover:bg-slate-50 dark:hover:bg-slate-800/60 cursor-pointer`,
|
||||
isCompact ? 'px-2 py-1' : 'p-2',
|
||||
!collapsed
|
||||
? 'bg-slate-200 dark:bg-slate-800 border-b-2 border-slate-900/10 dark:border-slate-300/10 '
|
||||
? 'bg-slate-50 dark:bg-slate-800/60 border-b dark:border-slate-700/60 dark:border-slate-300/10 '
|
||||
: ''
|
||||
)}
|
||||
onClick={handleCollapseToggle}
|
||||
>
|
||||
<div className="flex justify-between items-center">
|
||||
<div className="flex items-center">
|
||||
<h3 className="font-bold mr-2">{targetName}</h3>
|
||||
{collapsed && (
|
||||
<p className="text-slate-600 mr-2">
|
||||
{singleCommand ? singleCommand : targetConfiguration.executor}
|
||||
</p>
|
||||
<div className="flex items-center justify-between gap-2">
|
||||
<div className="flex items-center gap-2">
|
||||
{collapsed ? (
|
||||
<ChevronDownIcon className="h-3 w-3" />
|
||||
) : (
|
||||
<ChevronUpIcon className="h-3 w-3" />
|
||||
)}
|
||||
</div>
|
||||
<div className="flex items-center">
|
||||
{onViewInTaskGraph && (
|
||||
<EyeIcon
|
||||
className={`h-4 w-4 mr-2 ${
|
||||
collapsed
|
||||
? 'hidden group-hover:inline-block'
|
||||
: 'inline-block'
|
||||
}`}
|
||||
title="View in Task Graph"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onViewInTaskGraph({ projectName, targetName });
|
||||
}}
|
||||
/>
|
||||
<h3 className="font-medium dark:text-slate-300">{targetName}</h3>
|
||||
{collapsed &&
|
||||
targetConfiguration?.executor !== '@nx/js:release-publish' && (
|
||||
<p className="text-slate-400 text-sm">
|
||||
{singleCommand
|
||||
? singleCommand
|
||||
: targetConfiguration.executor}
|
||||
</p>
|
||||
)}
|
||||
{targetName === 'nx-release-publish' && (
|
||||
<Tooltip
|
||||
openAction="hover"
|
||||
strategy="fixed"
|
||||
content={(<PropertyInfoTooltip type="release" />) as any}
|
||||
>
|
||||
<span className="inline-flex">
|
||||
<Pill text="nx release" color="grey" />
|
||||
</span>
|
||||
</Tooltip>
|
||||
)}
|
||||
{targetConfiguration.cache && (
|
||||
<Tooltip
|
||||
@ -181,45 +187,72 @@ export const TargetConfigurationDetails = forwardRef(
|
||||
strategy="fixed"
|
||||
content={(<PropertyInfoTooltip type="cacheable" />) as any}
|
||||
>
|
||||
<span className="rounded-full inline-block text-xs bg-sky-500 dark:bg-sky-800 px-2 text-slate-50 mr-2">
|
||||
Cacheable
|
||||
<span className="inline-flex">
|
||||
<Pill text="Cacheable" color="green" />
|
||||
</span>
|
||||
</Tooltip>
|
||||
)}
|
||||
{onRunTarget && (
|
||||
<PlayIcon
|
||||
className="h-5 w-5 mr-2"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onRunTarget({ projectName, targetName });
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
{onViewInTaskGraph && (
|
||||
<button
|
||||
className="text-slate-600 dark:text-slate-300 text-sm ring-1 ring-inset ring-slate-400/40 dark:ring-slate-400/30 hover:bg-slate-200 dark:hover:bg-slate-700/60 p-1 bg-inherit rounded-md"
|
||||
// TODO: fix tooltip overflow in collapsed state
|
||||
data-tooltip={collapsed ? false : 'View in Task Graph'}
|
||||
data-tooltip-align-right
|
||||
>
|
||||
<EyeIcon
|
||||
className={`h-5 w-5 !cursor-pointer`}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onViewInTaskGraph({ projectName, targetName });
|
||||
}}
|
||||
/>
|
||||
</button>
|
||||
)}
|
||||
{collapsed ? (
|
||||
<ChevronDownIcon className="h-3 w-3" />
|
||||
) : (
|
||||
<ChevronUpIcon className="h-3 w-3" />
|
||||
|
||||
{onRunTarget && (
|
||||
<span
|
||||
className="text-slate-600 dark:text-slate-300 text-sm ring-1 ring-inset ring-slate-400/40 dark:ring-slate-400/30 hover:bg-slate-200 dark:hover:bg-slate-700/60 p-1 bg-inherit rounded-md"
|
||||
// TODO: fix tooltip overflow in collapsed state
|
||||
data-tooltip={collapsed ? false : 'Run Target'}
|
||||
data-tooltip-align-right
|
||||
>
|
||||
<PlayIcon
|
||||
className="h-5 w-5 !cursor-pointer"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onRunTarget({ projectName, targetName });
|
||||
}}
|
||||
/>
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{!collapsed && (
|
||||
<div className="flex items-center text-sm mt-2">
|
||||
<span className="flex-1 flex items-center">
|
||||
<div className="flex items-center text-sm mt-2 ml-5">
|
||||
<span className="flex-1 flex min-w-0 items-center">
|
||||
<SourceInfo
|
||||
data={sourceMap[`targets.${targetName}`]}
|
||||
propertyKey={`targets.${targetName}`}
|
||||
color="text-gray-500 dark:text-slate-400"
|
||||
/>
|
||||
</span>
|
||||
<code className="ml-4 bg-gray-100 dark:bg-gray-700 text-gray-800 dark:text-gray-300 font-mono px-2 py-1 rounded">
|
||||
nx run {projectName}:{targetName}
|
||||
</code>
|
||||
<span className="ml-2">
|
||||
<CopyToClipboard
|
||||
onCopy={() =>
|
||||
handleCopyClick(`nx run ${projectName}:${targetName}`)
|
||||
}
|
||||
/>
|
||||
</span>
|
||||
{targetName !== 'nx-release-publish' && (
|
||||
<div className="flex items-center gap-2">
|
||||
<code className="ml-4 bg-gray-100 dark:bg-gray-700 text-gray-800 dark:text-gray-300 font-mono px-2 py-1 rounded">
|
||||
nx run {projectName}:{targetName}
|
||||
</code>
|
||||
<span>
|
||||
<CopyToClipboard
|
||||
onCopy={() =>
|
||||
handleCopyClick(`nx run ${projectName}:${targetName}`)
|
||||
}
|
||||
tooltipAlignment="right"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</header>
|
||||
@ -229,7 +262,7 @@ export const TargetConfigurationDetails = forwardRef(
|
||||
<div className="mb-4 group">
|
||||
<h4 className="mb-4">
|
||||
{singleCommand ? (
|
||||
<span className="font-bold">
|
||||
<span className="font-medium">
|
||||
Command
|
||||
<span className="hidden group-hover:inline ml-2 mb-1">
|
||||
<CopyToClipboard
|
||||
@ -244,7 +277,7 @@ export const TargetConfigurationDetails = forwardRef(
|
||||
openAction="hover"
|
||||
content={(<PropertyInfoTooltip type="executors" />) as any}
|
||||
>
|
||||
<span className="font-bold">
|
||||
<span className="font-medium">
|
||||
<TooltipTriggerText>Executor</TooltipTriggerText>
|
||||
</span>
|
||||
</Tooltip>
|
||||
@ -252,15 +285,16 @@ export const TargetConfigurationDetails = forwardRef(
|
||||
</h4>
|
||||
<p className="pl-5">
|
||||
{executorLink ? (
|
||||
<ExternalLink
|
||||
href={executorLink ?? 'https://nx.dev/nx-api'}
|
||||
text={
|
||||
singleCommand
|
||||
? singleCommand
|
||||
: targetConfiguration.executor
|
||||
}
|
||||
title="View Documentation"
|
||||
/>
|
||||
<span>
|
||||
<ExternalLink
|
||||
href={executorLink ?? 'https://nx.dev/nx-api'}
|
||||
text={
|
||||
singleCommand
|
||||
? singleCommand
|
||||
: targetConfiguration.executor
|
||||
}
|
||||
/>
|
||||
</span>
|
||||
) : singleCommand ? (
|
||||
singleCommand
|
||||
) : (
|
||||
@ -276,7 +310,7 @@ export const TargetConfigurationDetails = forwardRef(
|
||||
openAction="hover"
|
||||
content={(<PropertyInfoTooltip type="inputs" />) as any}
|
||||
>
|
||||
<span className="font-bold">
|
||||
<span className="font-medium">
|
||||
<TooltipTriggerText>Inputs</TooltipTriggerText>
|
||||
</span>
|
||||
</Tooltip>
|
||||
@ -305,7 +339,7 @@ export const TargetConfigurationDetails = forwardRef(
|
||||
>
|
||||
<TargetConfigurationProperty data={input}>
|
||||
{sourceInfo && (
|
||||
<span className="hidden group-hover/line:inline pl-4">
|
||||
<span className="opacity-0 flex shrink-1 min-w-0 group-hover/line:opacity-100 transition-opacity duration-150 ease-in-out inline pl-4">
|
||||
<SourceInfo
|
||||
data={sourceInfo}
|
||||
propertyKey={`targets.${targetName}.inputs`}
|
||||
@ -326,7 +360,7 @@ export const TargetConfigurationDetails = forwardRef(
|
||||
openAction="hover"
|
||||
content={(<PropertyInfoTooltip type="outputs" />) as any}
|
||||
>
|
||||
<span className="font-bold">
|
||||
<span className="font-medium">
|
||||
<TooltipTriggerText>Outputs</TooltipTriggerText>
|
||||
</span>
|
||||
</Tooltip>
|
||||
@ -355,7 +389,7 @@ export const TargetConfigurationDetails = forwardRef(
|
||||
>
|
||||
<TargetConfigurationProperty data={output}>
|
||||
{sourceInfo && (
|
||||
<span className="hidden group-hover/line:inline pl-4">
|
||||
<span className="opacity-0 flex shrink-1 min-w-0 group-hover/line:opacity-100 transition-opacity duration-150 ease-in-out inline pl-4">
|
||||
<SourceInfo
|
||||
data={sourceInfo}
|
||||
propertyKey={`targets.${targetName}.outputs`}
|
||||
@ -376,11 +410,11 @@ export const TargetConfigurationDetails = forwardRef(
|
||||
openAction="hover"
|
||||
content={(<PropertyInfoTooltip type="dependsOn" />) as any}
|
||||
>
|
||||
<span className="font-bold">
|
||||
<span className="font-medium">
|
||||
<TooltipTriggerText>Depends On</TooltipTriggerText>
|
||||
</span>
|
||||
</Tooltip>
|
||||
<span className="hidden group-hover:inline ml-2 mb-1">
|
||||
<span className="opacity-0 group-hover/line:opacity-100 transition-opacity duration-150 ease-in-out inline pl-4">
|
||||
<CopyToClipboard
|
||||
onCopy={() =>
|
||||
handleCopyClick(
|
||||
@ -405,7 +439,7 @@ export const TargetConfigurationDetails = forwardRef(
|
||||
key={`dependsOn-${idx}`}
|
||||
>
|
||||
<TargetConfigurationProperty data={dep}>
|
||||
<span className="hidden group-hover/line:inline pl-4 h-6">
|
||||
<span className="opacity-0 flex shrink-1 min-w-0 group-hover/line:opacity-100 transition-opacity duration-150 ease-in-out inline pl-4">
|
||||
{sourceInfo && (
|
||||
<SourceInfo
|
||||
data={sourceInfo}
|
||||
@ -428,7 +462,7 @@ export const TargetConfigurationDetails = forwardRef(
|
||||
openAction="hover"
|
||||
content={(<PropertyInfoTooltip type="options" />) as any}
|
||||
>
|
||||
<span className="font-bold">
|
||||
<span className="font-medium">
|
||||
<TooltipTriggerText>Options</TooltipTriggerText>
|
||||
</span>
|
||||
</Tooltip>
|
||||
@ -443,7 +477,7 @@ export const TargetConfigurationDetails = forwardRef(
|
||||
`targets.${targetName}.options.${propertyName}`
|
||||
);
|
||||
return sourceInfo ? (
|
||||
<span className="pl-4">
|
||||
<span className="pl-4 flex shrink-1 min-w-0">
|
||||
<SourceInfo
|
||||
data={sourceInfo}
|
||||
propertyKey={`targets.${targetName}.options.${propertyName}`}
|
||||
@ -468,16 +502,17 @@ export const TargetConfigurationDetails = forwardRef(
|
||||
(<PropertyInfoTooltip type="configurations" />) as any
|
||||
}
|
||||
>
|
||||
<span className="font-bold">
|
||||
<span className="font-medium">
|
||||
<TooltipTriggerText>Configurations</TooltipTriggerText>
|
||||
</span>
|
||||
</Tooltip>{' '}
|
||||
{targetConfiguration.defaultConfiguration && (
|
||||
<span
|
||||
className="ml-3 font-bold rounded-full inline-block text-xs bg-sky-500 px-2 text-slate-50 mr-6"
|
||||
title="Default Configuration"
|
||||
>
|
||||
{targetConfiguration.defaultConfiguration}
|
||||
<span className="ml-3 cursor-help">
|
||||
<Pill
|
||||
tooltip="Default Configuration"
|
||||
text={targetConfiguration.defaultConfiguration}
|
||||
color="yellow"
|
||||
/>
|
||||
</span>
|
||||
)}
|
||||
</h4>
|
||||
@ -490,7 +525,7 @@ export const TargetConfigurationDetails = forwardRef(
|
||||
`targets.${targetName}.configurations.${propertyName}`
|
||||
);
|
||||
return sourceInfo ? (
|
||||
<span className="pl-4">
|
||||
<span className="pl-4 flex shrink-1 min-w-0">
|
||||
<SourceInfo
|
||||
data={sourceInfo}
|
||||
propertyKey={`targets.${targetName}.configurations.${propertyName}`}
|
||||
|
||||
@ -11,7 +11,7 @@ export function TargetConfigurationProperty({
|
||||
}: RenderPropertyProps): JSX.Element | null {
|
||||
if (typeof data === 'string') {
|
||||
return (
|
||||
<span>
|
||||
<span className="font-mono flex shrink-1 text-sm">
|
||||
{data}
|
||||
{children}
|
||||
</span>
|
||||
@ -20,7 +20,7 @@ export function TargetConfigurationProperty({
|
||||
return (
|
||||
<ul>
|
||||
{data.map((item, index) => (
|
||||
<li key={index}>
|
||||
<li key={index} className="font-mono flex shrink-1 text-sm">
|
||||
{String(item)}
|
||||
{children}
|
||||
</li>
|
||||
@ -31,7 +31,7 @@ export function TargetConfigurationProperty({
|
||||
return (
|
||||
<ul>
|
||||
{Object.entries(data).map(([key, value], index) => (
|
||||
<li key={index}>
|
||||
<li key={index} className="font-mono flex shrink-1 text-sm">
|
||||
<strong>{key}</strong>: {String(value)}
|
||||
{children}
|
||||
</li>
|
||||
|
||||
@ -6,7 +6,7 @@ export function TooltipTriggerText({
|
||||
children: string | ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<span className="underline underline-offset-8 decoration-2 decoration-dotted decoration-slate-700/50 dark:decoration-slate-400/50">
|
||||
<span className="underline cursor-help underline-offset-8 decoration-2 decoration-dotted decoration-slate-700/50 dark:decoration-slate-400/50">
|
||||
{children}
|
||||
</span>
|
||||
);
|
||||
|
||||
@ -13,7 +13,7 @@ export function ExternalLink({
|
||||
<a
|
||||
href={href}
|
||||
title={title}
|
||||
className="text-slate-500 dark:text-slate-300 hover:underline inline-flex items-center gap-2"
|
||||
className="text-slate-500 dark:text-slate-400 hover:underline inline-flex items-center gap-2"
|
||||
target="_blank"
|
||||
>
|
||||
{text} <ArrowTopRightOnSquareIcon className="w-4 h-4 inline" />
|
||||
|
||||
@ -8,7 +8,8 @@ type PropertyInfoTooltipType =
|
||||
| 'outputs'
|
||||
| 'dependsOn'
|
||||
| 'options'
|
||||
| 'configurations';
|
||||
| 'configurations'
|
||||
| 'release';
|
||||
|
||||
type PropertyInfoTooltipTypeOptions = {
|
||||
docsUrl: string;
|
||||
@ -73,6 +74,12 @@ const PROPERTY_INFO_TOOLTIP_TYPE_OPTIONS: Record<
|
||||
description:
|
||||
'Configurations are sets of Options to allow a Target to be used in different scenarios.',
|
||||
},
|
||||
release: {
|
||||
heading: 'nx release',
|
||||
description:
|
||||
"The nx-release-publish target is used to publish your project with nxrelease. Don't invoke this directly - use nx release publish instead.",
|
||||
docsUrl: 'https://nx.dev/nx-api/nx/documents/release',
|
||||
},
|
||||
};
|
||||
|
||||
export function PropertyInfoTooltip({ type }: PropertyInfoTooltipProps) {
|
||||
@ -80,10 +87,10 @@ export function PropertyInfoTooltip({ type }: PropertyInfoTooltipProps) {
|
||||
|
||||
return (
|
||||
<div className="text-sm text-slate-700 dark:text-slate-400 max-w-lg">
|
||||
<h4 className="flex justify-between items-center border-b text-base">
|
||||
<h4 className="flex justify-between items-center border-b border-slate-200 dark:border-slate-700/60 text-base">
|
||||
<span className="font-mono">{propertyInfo.heading}</span>
|
||||
</h4>
|
||||
<div className="flex flex-col font-mono border-b py-2">
|
||||
<div className="flex flex-col font-mono border-b border-slate-200 dark:border-slate-700/60 py-2">
|
||||
<p className="flex grow items-center gap-2 whitespace-pre-wrap">
|
||||
{propertyInfo.description}
|
||||
</p>
|
||||
|
||||
@ -24,7 +24,7 @@ export function SourcemapInfoToolTip({
|
||||
|
||||
return (
|
||||
<div className="text-sm text-slate-700 dark:text-slate-400 max-w-md sm:max-w-full">
|
||||
<div className="flex flex-col font-mono border-b py-2">
|
||||
<div className="flex flex-col font-mono border-b border-slate-200 dark:border-slate-700/60 py-2">
|
||||
<p className="flex grow items-center gap-2">
|
||||
<span className="font-bold">{isTarget ? 'Created' : 'Set'} by:</span>
|
||||
<span className="inline-flex grow justify-between items-center">
|
||||
|
||||
@ -26,6 +26,7 @@ import {
|
||||
useHover,
|
||||
useRole,
|
||||
safePolygon,
|
||||
useTransitionStyles,
|
||||
} from '@floating-ui/react';
|
||||
|
||||
export type TooltipProps = HTMLAttributes<HTMLDivElement> & {
|
||||
@ -76,6 +77,13 @@ export function Tooltip({
|
||||
],
|
||||
});
|
||||
|
||||
const { isMounted, styles: animationStyles } = useTransitionStyles(context, {
|
||||
duration: 200,
|
||||
initial: {
|
||||
opacity: openAction === 'hover' ? 0 : 1,
|
||||
},
|
||||
});
|
||||
|
||||
const staticSide: string =
|
||||
{
|
||||
top: 'bottom',
|
||||
@ -98,6 +106,7 @@ export function Tooltip({
|
||||
outsidePressEvent: 'mousedown',
|
||||
});
|
||||
const hover = useHover(context, {
|
||||
restMs: 300,
|
||||
enabled: openAction === 'hover',
|
||||
delay: { open: 0, close: 150 },
|
||||
handleClose: safePolygon({ buffer }),
|
||||
@ -121,7 +130,7 @@ export function Tooltip({
|
||||
{!externalReference && !!children
|
||||
? cloneElement(children, cloneProps)
|
||||
: children}
|
||||
{isOpen ? (
|
||||
{isOpen && isMounted ? (
|
||||
<div
|
||||
ref={refs.setFloating}
|
||||
style={{
|
||||
@ -129,6 +138,7 @@ export function Tooltip({
|
||||
top: showTooltipArrow ? y : y + 8 ?? 0,
|
||||
left: x ?? 0,
|
||||
width: 'max-content',
|
||||
...animationStyles,
|
||||
}}
|
||||
className="z-10 min-w-[250px] rounded-md border border-slate-500"
|
||||
{...getFloatingProps()}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user