feat(graph): rework pdv target section & remove unused code (#21159)
This commit is contained in:
parent
253c0ff2ab
commit
e38b0bb6f4
@ -81,6 +81,14 @@ export class ExternalApiImpl extends ExternalApi {
|
||||
}
|
||||
}
|
||||
|
||||
openProjectDetails(projectName: string, targetName?: string) {
|
||||
this.router.navigate(
|
||||
`/project-details/${encodeURIComponent(projectName)}${
|
||||
targetName ? `?expanded=${encodeURIComponent(targetName)}` : ''
|
||||
}`
|
||||
);
|
||||
}
|
||||
|
||||
focusProject(projectName: string) {
|
||||
this.router.navigate(`/projects/${encodeURIComponent(projectName)}`);
|
||||
}
|
||||
|
||||
@ -1,288 +0,0 @@
|
||||
import {
|
||||
ChevronDownIcon,
|
||||
ChevronRightIcon,
|
||||
EyeIcon,
|
||||
PlayIcon,
|
||||
} from '@heroicons/react/24/outline';
|
||||
import { getSourceInformation } from './get-source-information';
|
||||
import useMapState from './use-map-state';
|
||||
import {
|
||||
getExternalApiService,
|
||||
useEnvironmentConfig,
|
||||
useRouteConstructor,
|
||||
} from '@nx/graph/shared';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { get } from 'http';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
interface JsonLineRendererProps {
|
||||
jsonData: any;
|
||||
sourceMap: Record<string, string[]>;
|
||||
}
|
||||
|
||||
export function JsonLineRenderer(props: JsonLineRendererProps) {
|
||||
let collapsibleSections = new Map<number, number>();
|
||||
let lines: [string, number][] = [];
|
||||
let currentLine = 0;
|
||||
let lineToPropertyPathMap = new Map<number, string>();
|
||||
let lineToInteractionMap = new Map<
|
||||
number,
|
||||
{ target: string; configuration?: string }
|
||||
>();
|
||||
|
||||
const [getCollapsed, setCollapsed] = useMapState<number, boolean>();
|
||||
const { environment } = useEnvironmentConfig();
|
||||
const externalApiService = getExternalApiService();
|
||||
const navigate = useNavigate();
|
||||
const routeContructor = useRouteConstructor();
|
||||
|
||||
function add(value: string, depth: number) {
|
||||
if (lines.length === currentLine) {
|
||||
lines.push(['', depth]);
|
||||
}
|
||||
lines[currentLine] = [lines[currentLine][0] + value, depth];
|
||||
}
|
||||
|
||||
function processJson(
|
||||
jsonData: any,
|
||||
depth = 0,
|
||||
propertyPath = '',
|
||||
isLast = false
|
||||
) {
|
||||
if (Array.isArray(jsonData)) {
|
||||
const sectionStart = currentLine;
|
||||
add('[', depth);
|
||||
currentLine++;
|
||||
|
||||
jsonData.forEach((value, index) => {
|
||||
const newPropertyPath = `${
|
||||
propertyPath ? propertyPath + '.' : ''
|
||||
}${value}`;
|
||||
lineToPropertyPathMap.set(currentLine, newPropertyPath);
|
||||
|
||||
processJson(
|
||||
value,
|
||||
depth + 1,
|
||||
newPropertyPath,
|
||||
index === jsonData.length - 1
|
||||
);
|
||||
});
|
||||
|
||||
add(']', depth);
|
||||
if (!isLast) {
|
||||
add(',', depth);
|
||||
}
|
||||
const sectionEnd = currentLine;
|
||||
collapsibleSections.set(sectionStart, sectionEnd);
|
||||
currentLine++;
|
||||
} else if (jsonData && typeof jsonData === 'object') {
|
||||
const sectionStart = currentLine;
|
||||
add('{', depth);
|
||||
currentLine++;
|
||||
|
||||
Object.entries(jsonData).forEach(([key, value], index, array) => {
|
||||
// skip empty objects
|
||||
if (
|
||||
Object.keys(value as any).length === 0 &&
|
||||
typeof value === 'object'
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
// skip certain root properties
|
||||
if (
|
||||
depth === 0 &&
|
||||
(key === 'sourceRoot' ||
|
||||
key === 'name' ||
|
||||
key === '$schema' ||
|
||||
key === 'tags')
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
add(`"${key}": `, depth);
|
||||
|
||||
if (propertyPath === 'targets') {
|
||||
lineToInteractionMap.set(currentLine, { target: key });
|
||||
}
|
||||
if (propertyPath.match(/^targets\..*configurations$/)) {
|
||||
lineToInteractionMap.set(currentLine, {
|
||||
target: propertyPath.split('.')[1],
|
||||
configuration: key,
|
||||
});
|
||||
}
|
||||
|
||||
const newPropertyPath = `${
|
||||
propertyPath ? propertyPath + '.' : ''
|
||||
}${key}`;
|
||||
lineToPropertyPathMap.set(currentLine, newPropertyPath);
|
||||
|
||||
processJson(
|
||||
value,
|
||||
depth + 1,
|
||||
newPropertyPath,
|
||||
index === array.length - 1
|
||||
);
|
||||
});
|
||||
|
||||
add('}', depth);
|
||||
if (!isLast) {
|
||||
add(',', depth);
|
||||
}
|
||||
const sectionEnd = currentLine;
|
||||
collapsibleSections.set(sectionStart, sectionEnd);
|
||||
currentLine++;
|
||||
} else {
|
||||
add(`"${jsonData}"`, depth);
|
||||
if (!isLast) {
|
||||
add(',', depth);
|
||||
}
|
||||
currentLine++;
|
||||
}
|
||||
}
|
||||
|
||||
processJson(props.jsonData);
|
||||
|
||||
console.log(lineToInteractionMap);
|
||||
// start off with all targets & configurations collapsed~
|
||||
useEffect(() => {
|
||||
for (const line of lineToInteractionMap.keys()) {
|
||||
if (!getCollapsed(line)) {
|
||||
setCollapsed(line, true);
|
||||
}
|
||||
}
|
||||
}, []);
|
||||
|
||||
function toggleCollapsed(index: number) {
|
||||
setCollapsed(index, !getCollapsed(index));
|
||||
}
|
||||
|
||||
function lineIsCollapsed(index: number) {
|
||||
for (const [start, end] of collapsibleSections) {
|
||||
if (index > start && index < end) {
|
||||
if (getCollapsed(start)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function runTarget({
|
||||
target,
|
||||
configuration,
|
||||
}: {
|
||||
target: string;
|
||||
configuration?: string;
|
||||
}) {
|
||||
const projectName = props.jsonData.name;
|
||||
|
||||
externalApiService.postEvent({
|
||||
type: 'run-task',
|
||||
payload: { taskId: `${projectName}:${target}` },
|
||||
});
|
||||
}
|
||||
|
||||
function viewInTaskGraph({
|
||||
target,
|
||||
configuration,
|
||||
}: {
|
||||
target: string;
|
||||
configuration?: string;
|
||||
}) {
|
||||
const projectName = props.jsonData.name;
|
||||
if (environment === 'nx-console') {
|
||||
externalApiService.postEvent({
|
||||
type: 'open-task-graph',
|
||||
payload: {
|
||||
projectName: projectName,
|
||||
targetName: target,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
navigate(
|
||||
routeContructor(
|
||||
{
|
||||
pathname: `/tasks/${encodeURIComponent(target)}`,
|
||||
search: `?projects=${encodeURIComponent(projectName)}`,
|
||||
},
|
||||
true
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="overflow-auto w-full h-full flex">
|
||||
<div className="h-fit min-h-full w-12 shrink-0 pr-2 border-solid border-r-2 border-slate-700">
|
||||
{lines.map(([text, indentation], index) => {
|
||||
if (
|
||||
lineIsCollapsed(index) ||
|
||||
index === 0 ||
|
||||
index === lines.length - 1
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
const canCollapse =
|
||||
collapsibleSections.has(index) &&
|
||||
collapsibleSections.get(index)! - index > 1;
|
||||
const interaction = lineToInteractionMap.get(index);
|
||||
return (
|
||||
<div className="flex justify-end items-center h-6">
|
||||
{interaction?.target && !interaction?.configuration && (
|
||||
<EyeIcon
|
||||
className="h-4 w-4"
|
||||
onClick={() => viewInTaskGraph(interaction!)}
|
||||
/>
|
||||
)}
|
||||
{environment === 'nx-console' && interaction?.target && (
|
||||
<PlayIcon
|
||||
className="h-4 w-4"
|
||||
onClick={() => runTarget(interaction!)}
|
||||
/>
|
||||
)}
|
||||
|
||||
{canCollapse && (
|
||||
<div onClick={() => toggleCollapsed(index)} className="h-4 w-4">
|
||||
{getCollapsed(index) ? (
|
||||
<ChevronRightIcon />
|
||||
) : (
|
||||
<ChevronDownIcon />
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<div className="pl-2">
|
||||
{lines.map(([text, indentation], index) => {
|
||||
if (
|
||||
lineIsCollapsed(index) ||
|
||||
index === 0 ||
|
||||
index === lines.length - 1
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
const propertyPathAtLine = lineToPropertyPathMap.get(index);
|
||||
const sourceInformation = propertyPathAtLine
|
||||
? getSourceInformation(props.sourceMap, propertyPathAtLine)
|
||||
: '';
|
||||
return (
|
||||
<pre
|
||||
style={{ paddingLeft: `${indentation}rem` }}
|
||||
className="group truncate hover:bg-slate-800 h-6"
|
||||
>
|
||||
{text}
|
||||
{getCollapsed(index) ? '...' : ''}
|
||||
|
||||
<span className="ml-16 hidden group-hover:inline-block text-sm text-slate-500">
|
||||
{sourceInformation}
|
||||
</span>
|
||||
</pre>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -1,18 +1,17 @@
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
|
||||
import { useNavigate, useRouteLoaderData } from 'react-router-dom';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
/* eslint-disable @nx/enforce-module-boundaries */
|
||||
// nx-ignore-next-line
|
||||
import { ProjectGraphProjectNode } from '@nx/devkit';
|
||||
|
||||
import { EyeIcon } from '@heroicons/react/24/outline';
|
||||
import {
|
||||
getExternalApiService,
|
||||
useEnvironmentConfig,
|
||||
useRouteConstructor,
|
||||
} from '@nx/graph/shared';
|
||||
import { JsonLineRenderer } from './json-line-renderer';
|
||||
import { EyeIcon } from '@heroicons/react/24/outline';
|
||||
import PropertyRenderer from './property-renderer';
|
||||
import Target from './target';
|
||||
|
||||
@ -28,7 +27,7 @@ export function ProjectDetails({
|
||||
},
|
||||
sourceMap,
|
||||
}: ProjectDetailsProps) {
|
||||
const { environment } = useEnvironmentConfig();
|
||||
const environment = useEnvironmentConfig()?.environment;
|
||||
const externalApiService = getExternalApiService();
|
||||
const navigate = useNavigate();
|
||||
const routeContructor = useRouteConstructor();
|
||||
@ -46,35 +45,6 @@ export function ProjectDetails({
|
||||
}
|
||||
};
|
||||
|
||||
// const projectDataSorted = sortObjectWithTargetsFirst(projectData);
|
||||
// return (
|
||||
// <div className="flex flex-col w-full h-full">
|
||||
// <div className="flex">
|
||||
// <div className="w-12 pr-2 border-r-2 border-solid border-slate-700">
|
||||
// <EyeIcon
|
||||
// className="h-6 w-6 ml-3 mt-3"
|
||||
// onClick={viewInProjectGraph}
|
||||
// ></EyeIcon>
|
||||
// </div>
|
||||
// <div className="pl-6 pb-6">
|
||||
// <h1 className="text-4xl flex items-center">
|
||||
// <span>{name}</span>
|
||||
// </h1>
|
||||
// <div className="flex gap-2">
|
||||
// <span className="text-slate-500 text-xl"> {root}</span>
|
||||
|
||||
// {projectData.tags?.map((tag) => (
|
||||
// <div className="dark:bg-sky-500 text-white rounded px-1">
|
||||
// {tag}
|
||||
// </div>
|
||||
// ))}
|
||||
// </div>
|
||||
// </div>
|
||||
// </div>
|
||||
// {JsonLineRenderer({ jsonData: projectDataSorted, sourceMap })}
|
||||
// </div>
|
||||
// );
|
||||
|
||||
return (
|
||||
<div className="m-4 overflow-auto w-full">
|
||||
<h1 className="text-2xl flex items-center gap-2">
|
||||
@ -89,15 +59,17 @@ export function ProjectDetails({
|
||||
</h2>
|
||||
<div>
|
||||
<div className="mb-2">
|
||||
<h2 className="text-xl">Targets</h2>
|
||||
<h2 className="text-xl mb-2">Targets</h2>
|
||||
{Object.entries(projectData.targets ?? {}).map(
|
||||
([targetName, target]) =>
|
||||
Target({
|
||||
([targetName, target]) => {
|
||||
const props = {
|
||||
projectName: name,
|
||||
targetName: targetName,
|
||||
targetConfiguration: target,
|
||||
sourceMap,
|
||||
})
|
||||
};
|
||||
return <Target {...props} />;
|
||||
}
|
||||
)}
|
||||
</div>
|
||||
{Object.entries(projectData).map(([key, value]) => {
|
||||
@ -123,22 +95,4 @@ export function ProjectDetails({
|
||||
);
|
||||
}
|
||||
|
||||
// function sortObjectWithTargetsFirst(obj: any) {
|
||||
// let sortedObj: any = {};
|
||||
|
||||
// // If 'targets' exists, set it as the first property
|
||||
// if (obj.hasOwnProperty('targets')) {
|
||||
// sortedObj.targets = obj.targets;
|
||||
// }
|
||||
|
||||
// // Copy the rest of the properties
|
||||
// for (let key in obj) {
|
||||
// if (key !== 'targets') {
|
||||
// sortedObj[key] = obj[key];
|
||||
// }
|
||||
// }
|
||||
|
||||
// return sortedObj;
|
||||
// }
|
||||
|
||||
export default ProjectDetails;
|
||||
|
||||
@ -54,7 +54,7 @@ type PropertValueRendererProps = PropertyRendererProps & {
|
||||
function PropertyValueRenderer(props: PropertValueRendererProps) {
|
||||
const { propertyKey, propertyValue, sourceMap, keyPrefix, nested } = props;
|
||||
|
||||
if (Array.isArray(propertyValue) && propertyValue.length) {
|
||||
if (propertyValue && Array.isArray(propertyValue) && propertyValue.length) {
|
||||
return (
|
||||
<div className="ml-3">
|
||||
{nested && renderOpening(propertyValue)}
|
||||
@ -107,7 +107,7 @@ function PropertyValueRenderer(props: PropertValueRendererProps) {
|
||||
}
|
||||
|
||||
function renderOpening(value: any): string {
|
||||
return Array.isArray(value) && value.length
|
||||
return value && Array.isArray(value) && value.length
|
||||
? '['
|
||||
: value && typeof value === 'object'
|
||||
? '{'
|
||||
@ -115,7 +115,7 @@ function renderOpening(value: any): string {
|
||||
}
|
||||
|
||||
function renderClosing(value: any): string {
|
||||
return Array.isArray(value) && value.length
|
||||
return value && Array.isArray(value) && value.length
|
||||
? '],'
|
||||
: value && typeof value === 'object'
|
||||
? '},'
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
/* eslint-disable @nx/enforce-module-boundaries */
|
||||
// nx-ignore-next-line
|
||||
import {
|
||||
ChevronDownIcon,
|
||||
ChevronUpIcon,
|
||||
EyeIcon,
|
||||
PencilSquareIcon,
|
||||
PlayIcon,
|
||||
} from '@heroicons/react/24/outline';
|
||||
|
||||
@ -13,8 +14,10 @@ import {
|
||||
useEnvironmentConfig,
|
||||
useRouteConstructor,
|
||||
} from '@nx/graph/shared';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import PropertyRenderer from './property-renderer';
|
||||
import { Fence } from '@nx/shared-ui-fence';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useNavigate, useSearchParams } from 'react-router-dom';
|
||||
import { FadingCollapsible } from './ui/fading-collapsible.component';
|
||||
|
||||
/* eslint-disable-next-line */
|
||||
export interface TargetProps {
|
||||
@ -24,16 +27,61 @@ export interface TargetProps {
|
||||
sourceMap: Record<string, string[]>;
|
||||
}
|
||||
|
||||
export function Target(props: TargetProps) {
|
||||
const { environment } = useEnvironmentConfig();
|
||||
export function Target({
|
||||
projectName,
|
||||
targetName,
|
||||
targetConfiguration,
|
||||
sourceMap,
|
||||
}: TargetProps) {
|
||||
const environment = useEnvironmentConfig()?.environment;
|
||||
const externalApiService = getExternalApiService();
|
||||
const navigate = useNavigate();
|
||||
const routeContructor = useRouteConstructor();
|
||||
|
||||
const [searchParams, setSearchParams] = useSearchParams();
|
||||
const [collapsed, setCollapsed] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
const expandedSections = searchParams.get('expanded')?.split(',') || [];
|
||||
setCollapsed(!expandedSections.includes(targetName));
|
||||
}, [searchParams, targetName]);
|
||||
|
||||
function toggleCollapsed() {
|
||||
setCollapsed((prevState) => {
|
||||
const newState = !prevState;
|
||||
setSearchParams((currentSearchParams) => {
|
||||
const expandedSections =
|
||||
currentSearchParams.get('expanded')?.split(',') || [];
|
||||
if (newState) {
|
||||
const newExpandedSections = expandedSections.filter(
|
||||
(section) => section !== targetName
|
||||
);
|
||||
updateSearchParams(currentSearchParams, newExpandedSections);
|
||||
} else {
|
||||
if (!expandedSections.includes(targetName)) {
|
||||
expandedSections.push(targetName);
|
||||
updateSearchParams(currentSearchParams, expandedSections);
|
||||
}
|
||||
}
|
||||
return currentSearchParams;
|
||||
});
|
||||
|
||||
return newState;
|
||||
});
|
||||
}
|
||||
|
||||
function updateSearchParams(params: URLSearchParams, sections: string[]) {
|
||||
if (sections.length === 0) {
|
||||
params.delete('expanded');
|
||||
} else {
|
||||
params.set('expanded', sections.join(','));
|
||||
}
|
||||
}
|
||||
|
||||
const runTarget = () => {
|
||||
externalApiService.postEvent({
|
||||
type: 'run-task',
|
||||
payload: { taskId: `${props.projectName}:${props.targetName}` },
|
||||
payload: { taskId: `${projectName}:${targetName}` },
|
||||
});
|
||||
};
|
||||
|
||||
@ -42,16 +90,16 @@ export function Target(props: TargetProps) {
|
||||
externalApiService.postEvent({
|
||||
type: 'open-task-graph',
|
||||
payload: {
|
||||
projectName: props.projectName,
|
||||
targetName: props.targetName,
|
||||
projectName: projectName,
|
||||
targetName: targetName,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
navigate(
|
||||
routeContructor(
|
||||
{
|
||||
pathname: `/tasks/${encodeURIComponent(props.targetName)}`,
|
||||
search: `?projects=${encodeURIComponent(props.projectName)}`,
|
||||
pathname: `/tasks/${encodeURIComponent(targetName)}`,
|
||||
search: `?projects=${encodeURIComponent(projectName)}`,
|
||||
},
|
||||
true
|
||||
)
|
||||
@ -59,73 +107,163 @@ export function Target(props: TargetProps) {
|
||||
}
|
||||
};
|
||||
|
||||
const overrideTarget = () => {
|
||||
externalApiService.postEvent({
|
||||
type: 'override-target',
|
||||
payload: {
|
||||
projectName: props.projectName,
|
||||
targetName: props.targetName,
|
||||
targetConfigString: JSON.stringify(props.targetConfiguration),
|
||||
},
|
||||
});
|
||||
};
|
||||
const shouldRenderOptions =
|
||||
targetConfiguration.options &&
|
||||
(typeof targetConfiguration.options === 'object'
|
||||
? Object.keys(targetConfiguration.options).length
|
||||
: true);
|
||||
|
||||
const shouldDisplayOverrideTarget = () => {
|
||||
return (
|
||||
environment === 'nx-console' &&
|
||||
Object.entries(props.sourceMap ?? {})
|
||||
.filter(([key]) => key.startsWith(`targets.${props.targetName}`))
|
||||
.every(([, value]) => value[1] !== 'nx-core-build-project-json-nodes')
|
||||
);
|
||||
};
|
||||
const shouldRenderConfigurations =
|
||||
targetConfiguration.configurations &&
|
||||
(typeof targetConfiguration.configurations === 'object'
|
||||
? Object.keys(targetConfiguration.configurations).length
|
||||
: true);
|
||||
|
||||
const targetConfigurationSortedAndFiltered = Object.entries(
|
||||
props.targetConfiguration
|
||||
)
|
||||
.filter(([, value]) => {
|
||||
return (
|
||||
value &&
|
||||
(Array.isArray(value) ? value.length : true) &&
|
||||
(typeof value === 'object' ? Object.keys(value).length : true)
|
||||
);
|
||||
})
|
||||
.sort(([a], [b]) => {
|
||||
const order = ['executor', 'inputs', 'outputs'];
|
||||
const indexA = order.indexOf(a);
|
||||
const indexB = order.indexOf(b);
|
||||
|
||||
if (indexA !== -1 && indexB !== -1) {
|
||||
return indexA - indexB;
|
||||
} else if (indexA !== -1) {
|
||||
return -1;
|
||||
} else if (indexB !== -1) {
|
||||
return 1;
|
||||
} else {
|
||||
return a.localeCompare(b);
|
||||
}
|
||||
});
|
||||
return (
|
||||
<div className="ml-3 mb-3">
|
||||
<h3 className="text-lg font-bold flex items-center gap-2">
|
||||
{props.targetName}{' '}
|
||||
{environment === 'nx-console' && (
|
||||
<PlayIcon className="h-5 w-5" onClick={runTarget} />
|
||||
)}
|
||||
<EyeIcon className="h-5 w-5" onClick={viewInTaskGraph}></EyeIcon>
|
||||
{shouldDisplayOverrideTarget() && (
|
||||
<PencilSquareIcon className="h-5 w-5" onClick={overrideTarget} />
|
||||
)}
|
||||
</h3>
|
||||
<div className="ml-3">
|
||||
{targetConfigurationSortedAndFiltered.map(([key, value]) =>
|
||||
PropertyRenderer({
|
||||
propertyKey: key,
|
||||
propertyValue: value,
|
||||
keyPrefix: `targets.${props.targetName}`,
|
||||
sourceMap: props.sourceMap,
|
||||
})
|
||||
)}
|
||||
<div className="ml-3 mb-3 rounded-md border border-slate-500 relative overflow-hidden">
|
||||
{/* header */}
|
||||
<div className="group hover:bg-slate-800 px-2 cursor-pointer ">
|
||||
<h3
|
||||
className="text-lg font-bold flex items-center gap-2"
|
||||
onClick={toggleCollapsed}
|
||||
>
|
||||
{targetName}{' '}
|
||||
<h4 className="text-sm text-slate-600">
|
||||
{targetConfiguration?.command ??
|
||||
targetConfiguration.options?.command ??
|
||||
targetConfiguration.executor}
|
||||
</h4>
|
||||
<span
|
||||
className={
|
||||
collapsed ? 'hidden group-hover:inline-flex' : 'inline-flex'
|
||||
}
|
||||
>
|
||||
<span
|
||||
className={`inline-flex justify-center rounded-md p-1 hover:bg-slate-100 hover:dark:bg-slate-700
|
||||
}`}
|
||||
>
|
||||
<EyeIcon
|
||||
className="h-4 w-4"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
viewInTaskGraph();
|
||||
}}
|
||||
></EyeIcon>
|
||||
</span>
|
||||
{environment === 'nx-console' && (
|
||||
<span
|
||||
className={`inline-flex justify-center rounded-md p-1 hover:bg-slate-100 hover:dark:bg-slate-700
|
||||
}`}
|
||||
>
|
||||
<PlayIcon
|
||||
className="h-4 w-4"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
runTarget();
|
||||
}}
|
||||
/>
|
||||
</span>
|
||||
)}
|
||||
</span>
|
||||
{targetConfiguration.cache && (
|
||||
<span className="rounded-full inline-block text-xs bg-sky-500 px-2 text-slate-50 ml-auto mr-6">
|
||||
Cacheable
|
||||
</span>
|
||||
)}
|
||||
</h3>
|
||||
<div className="absolute top-2 right-3" onClick={toggleCollapsed}>
|
||||
{collapsed ? (
|
||||
<ChevronUpIcon className="h-3 w-3" />
|
||||
) : (
|
||||
<ChevronDownIcon className="h-3 w-3" />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{/* body */}
|
||||
{!collapsed && (
|
||||
<div className="pl-5 text-base pb-6 pt-2 ">
|
||||
{targetConfiguration.inputs && (
|
||||
<>
|
||||
<h4 className="font-bold">Inputs</h4>
|
||||
<ul className="list-disc pl-5">
|
||||
{targetConfiguration.inputs.map((input) => (
|
||||
<li> {input.toString()} </li>
|
||||
))}
|
||||
</ul>
|
||||
</>
|
||||
)}
|
||||
{targetConfiguration.outputs && (
|
||||
<>
|
||||
<h4 className="font-bold pt-2">Outputs</h4>
|
||||
<ul className="list-disc pl-5">
|
||||
{targetConfiguration.outputs?.map((output) => (
|
||||
<li> {output.toString()} </li>
|
||||
)) ?? <span>no outputs</span>}
|
||||
</ul>
|
||||
</>
|
||||
)}
|
||||
{targetConfiguration.dependsOn && (
|
||||
<>
|
||||
<h4 className="font-bold py-2">Depends On</h4>
|
||||
<ul className="list-disc pl-5">
|
||||
{targetConfiguration.dependsOn.map((dep) => (
|
||||
<li> {dep.toString()} </li>
|
||||
))}
|
||||
</ul>
|
||||
</>
|
||||
)}
|
||||
{shouldRenderOptions ? (
|
||||
<>
|
||||
<h4 className="font-bold py-2">Options</h4>
|
||||
<FadingCollapsible>
|
||||
<Fence
|
||||
language="json"
|
||||
command=""
|
||||
path=""
|
||||
fileName=""
|
||||
highlightLines={[]}
|
||||
lineGroups={{}}
|
||||
enableCopy={true}
|
||||
>
|
||||
{JSON.stringify(targetConfiguration.options, null, 2)}
|
||||
</Fence>
|
||||
</FadingCollapsible>
|
||||
</>
|
||||
) : (
|
||||
''
|
||||
)}
|
||||
{shouldRenderConfigurations ? (
|
||||
<>
|
||||
<h4 className="font-bold py-2">
|
||||
Configurations{' '}
|
||||
{targetConfiguration.defaultConfiguration && (
|
||||
<span
|
||||
className="ml-3 rounded-full inline-block text-xs bg-sky-500 px-2 text-slate-50 mr-6"
|
||||
title="Default Configuration"
|
||||
>
|
||||
{targetConfiguration.defaultConfiguration}
|
||||
</span>
|
||||
)}
|
||||
</h4>
|
||||
<FadingCollapsible>
|
||||
<Fence
|
||||
language="json"
|
||||
command=""
|
||||
path=""
|
||||
fileName=""
|
||||
highlightLines={[]}
|
||||
lineGroups={{}}
|
||||
enableCopy={true}
|
||||
>
|
||||
{JSON.stringify(targetConfiguration.configurations, null, 2)}
|
||||
</Fence>
|
||||
</FadingCollapsible>
|
||||
</>
|
||||
) : (
|
||||
''
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -0,0 +1,55 @@
|
||||
import { ArrowDownIcon, ArrowUpIcon } from '@heroicons/react/24/outline';
|
||||
import { ReactNode, useEffect, useRef, useState } from 'react';
|
||||
|
||||
export function FadingCollapsible({ children }: { children: ReactNode }) {
|
||||
const [collapsed, setCollapsed] = useState(true);
|
||||
const [isCollapsible, setIsCollapsible] = useState(true);
|
||||
const contentRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (contentRef.current) {
|
||||
setIsCollapsible(contentRef.current.offsetHeight > 300);
|
||||
}
|
||||
}, [contentRef, children]);
|
||||
|
||||
function toggleCollapsed() {
|
||||
setCollapsed(!collapsed);
|
||||
}
|
||||
|
||||
const fadeStyles =
|
||||
collapsed && isCollapsible
|
||||
? {
|
||||
maxHeight: '150px',
|
||||
maskImage: 'linear-gradient(to bottom, black 40%, transparent)',
|
||||
WebkitMaskImage: 'linear-gradient(to bottom, black 40%, transparent)',
|
||||
}
|
||||
: {};
|
||||
return (
|
||||
<div
|
||||
className={`relative overflow-hidden ${
|
||||
collapsed && isCollapsible ? 'cursor-pointer' : 'max-h-full'
|
||||
}`}
|
||||
onClick={() => collapsed && isCollapsible && toggleCollapsed()}
|
||||
>
|
||||
<div
|
||||
className={`${
|
||||
collapsed && isCollapsible ? 'hover:bg-slate-700' : ''
|
||||
} rounded-md`}
|
||||
style={fadeStyles}
|
||||
>
|
||||
<div ref={contentRef}>{children}</div>
|
||||
</div>
|
||||
{isCollapsible && (
|
||||
<div
|
||||
className="h-4 w-4 absolute bottom-2 right-1/2 cursor-pointer"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
toggleCollapsed();
|
||||
}}
|
||||
>
|
||||
{collapsed ? <ArrowDownIcon /> : <ArrowUpIcon />}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -1,21 +0,0 @@
|
||||
import { useState, useCallback } from 'react';
|
||||
|
||||
function useMapState<K, V>(initialMap: Map<K, V> = new Map()) {
|
||||
const [map, setMap] = useState(new Map(initialMap));
|
||||
|
||||
// Function to set a key-value pair in the map
|
||||
const setKey = useCallback((key: K, value: V) => {
|
||||
setMap((prevMap) => {
|
||||
const newMap = new Map(prevMap);
|
||||
newMap.set(key, value);
|
||||
return newMap;
|
||||
});
|
||||
}, []);
|
||||
|
||||
// Function to get a value by key from the map
|
||||
const getKey = useCallback((key: K) => map.get(key), [map]);
|
||||
|
||||
return [getKey, setKey] as const;
|
||||
}
|
||||
|
||||
export default useMapState;
|
||||
@ -6,7 +6,8 @@
|
||||
"node",
|
||||
"@nx/react/typings/cssmodule.d.ts",
|
||||
"@nx/react/typings/image.d.ts"
|
||||
]
|
||||
],
|
||||
"lib": ["DOM"]
|
||||
},
|
||||
"exclude": [
|
||||
"jest.config.ts",
|
||||
|
||||
12
nx-dev/shared-ui-fence/.babelrc
Normal file
12
nx-dev/shared-ui-fence/.babelrc
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"presets": [
|
||||
[
|
||||
"@nx/react/babel",
|
||||
{
|
||||
"runtime": "automatic",
|
||||
"useBuiltIns": "usage"
|
||||
}
|
||||
]
|
||||
],
|
||||
"plugins": []
|
||||
}
|
||||
18
nx-dev/shared-ui-fence/.eslintrc.json
Normal file
18
nx-dev/shared-ui-fence/.eslintrc.json
Normal file
@ -0,0 +1,18 @@
|
||||
{
|
||||
"extends": ["plugin:@nx/react", "../../.eslintrc.json"],
|
||||
"ignorePatterns": ["!**/*"],
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
|
||||
"rules": {}
|
||||
},
|
||||
{
|
||||
"files": ["*.ts", "*.tsx"],
|
||||
"rules": {}
|
||||
},
|
||||
{
|
||||
"files": ["*.js", "*.jsx"],
|
||||
"rules": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
7
nx-dev/shared-ui-fence/README.md
Normal file
7
nx-dev/shared-ui-fence/README.md
Normal file
@ -0,0 +1,7 @@
|
||||
# shared-ui-fence
|
||||
|
||||
This library was generated with [Nx](https://nx.dev).
|
||||
|
||||
## Running unit tests
|
||||
|
||||
Run `nx test shared-ui-fence` to execute the unit tests via [Jest](https://jestjs.io).
|
||||
10
nx-dev/shared-ui-fence/jest.config.ts
Normal file
10
nx-dev/shared-ui-fence/jest.config.ts
Normal file
@ -0,0 +1,10 @@
|
||||
/* eslint-disable */
|
||||
export default {
|
||||
displayName: 'shared-ui-fence',
|
||||
preset: '../../jest.preset.js',
|
||||
transform: {
|
||||
'^.+\\.[tj]sx?$': ['babel-jest', { presets: ['@nx/react/babel'] }],
|
||||
},
|
||||
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
|
||||
coverageDirectory: '../../coverage/nx-dev/shared-ui-fence',
|
||||
};
|
||||
20
nx-dev/shared-ui-fence/project.json
Normal file
20
nx-dev/shared-ui-fence/project.json
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"name": "shared-ui-fence",
|
||||
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
||||
"sourceRoot": "nx-dev/shared-ui-fence/src",
|
||||
"projectType": "library",
|
||||
"tags": [],
|
||||
"targets": {
|
||||
"lint": {
|
||||
"executor": "@nx/eslint:lint"
|
||||
},
|
||||
"test": {
|
||||
"executor": "@nx/jest:jest",
|
||||
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
|
||||
"options": {
|
||||
"jestConfig": "nx-dev/shared-ui-fence/jest.config.ts",
|
||||
"passWithNoTests": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
3
nx-dev/shared-ui-fence/src/index.ts
Normal file
3
nx-dev/shared-ui-fence/src/index.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export * from './lib/fence.component';
|
||||
export { TerminalOutput } from './lib/fences/terminal-output.component';
|
||||
export { TerminalShellWrapper } from './lib/fences/terminal-shell.component';
|
||||
@ -11,8 +11,7 @@ import { CopyToClipboard } from 'react-copy-to-clipboard';
|
||||
import SyntaxHighlighter from 'react-syntax-highlighter';
|
||||
import { CodeOutput } from './fences/code-output.component';
|
||||
import { TerminalOutput } from './fences/terminal-output.component';
|
||||
import { useRouter } from 'next/router';
|
||||
import { Selector } from '@nx/nx-dev/ui-common';
|
||||
import { Selector } from '@nx/shared-ui-selector';
|
||||
|
||||
function resolveLanguage(lang: string) {
|
||||
switch (lang) {
|
||||
@ -57,32 +56,6 @@ function CodeWrapper(options: {
|
||||
);
|
||||
}
|
||||
|
||||
const useUrlHash = (initialValue: string) => {
|
||||
const router = useRouter();
|
||||
const [hash, setHash] = useState(initialValue);
|
||||
|
||||
const updateHash = (str: string) => {
|
||||
if (!str) return;
|
||||
setHash(str.split('#')[1]);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const onWindowHashChange = () => updateHash(window.location.hash);
|
||||
const onNextJSHashChange = (url: string) => updateHash(url);
|
||||
|
||||
router.events.on('hashChangeStart', onNextJSHashChange);
|
||||
window.addEventListener('hashchange', onWindowHashChange);
|
||||
window.addEventListener('load', onWindowHashChange);
|
||||
return () => {
|
||||
router.events.off('hashChangeStart', onNextJSHashChange);
|
||||
window.removeEventListener('load', onWindowHashChange);
|
||||
window.removeEventListener('hashchange', onWindowHashChange);
|
||||
};
|
||||
}, [router.asPath, router.events]);
|
||||
|
||||
return hash;
|
||||
};
|
||||
|
||||
// pre-process the highlightLines to expand ranges like
|
||||
// ["8-10", 19, 22] => [8,9,10,19,22]
|
||||
function processHighlightLines(highlightLines: any): number[] {
|
||||
@ -109,6 +82,19 @@ function processHighlightLines(highlightLines: any): number[] {
|
||||
);
|
||||
}
|
||||
|
||||
export interface FenceProps {
|
||||
children: string;
|
||||
command: string;
|
||||
path: string;
|
||||
fileName: string;
|
||||
highlightLines: number[];
|
||||
lineGroups: Record<string, number[]>;
|
||||
language: string;
|
||||
enableCopy: boolean;
|
||||
selectedLineGroup?: string;
|
||||
onLineGroupSelectionChange?: (selection: string) => void;
|
||||
}
|
||||
|
||||
export function Fence({
|
||||
children,
|
||||
command,
|
||||
@ -118,19 +104,9 @@ export function Fence({
|
||||
highlightLines,
|
||||
language,
|
||||
enableCopy,
|
||||
}: {
|
||||
children: string;
|
||||
command: string;
|
||||
path: string;
|
||||
fileName: string;
|
||||
highlightLines: number[];
|
||||
lineGroups: Record<string, number[]>;
|
||||
language: string;
|
||||
enableCopy: boolean;
|
||||
}) {
|
||||
const { push, asPath } = useRouter();
|
||||
const hash = decodeURIComponent(useUrlHash(''));
|
||||
|
||||
selectedLineGroup,
|
||||
onLineGroupSelectionChange,
|
||||
}: FenceProps) {
|
||||
if (highlightLines) {
|
||||
highlightLines = processHighlightLines(highlightLines);
|
||||
}
|
||||
@ -138,7 +114,9 @@ export function Fence({
|
||||
function lineNumberStyle(lineNumber: number) {
|
||||
if (
|
||||
(highlightLines && highlightLines.includes(lineNumber)) ||
|
||||
(lineGroups[hash] && lineGroups[hash].includes(lineNumber))
|
||||
(selectedLineGroup &&
|
||||
lineGroups[selectedLineGroup] &&
|
||||
lineGroups[selectedLineGroup].includes(lineNumber))
|
||||
) {
|
||||
return {
|
||||
fontSize: 0,
|
||||
@ -168,7 +146,7 @@ export function Fence({
|
||||
});
|
||||
}
|
||||
let selectedOption =
|
||||
highlightOptions.find((option) => option.value === hash) ||
|
||||
highlightOptions.find((option) => option.value === selectedLineGroup) ||
|
||||
highlightOptions[0];
|
||||
const [copied, setCopied] = useState(false);
|
||||
useEffect(() => {
|
||||
@ -185,77 +163,75 @@ export function Fence({
|
||||
const showRescopeMessage =
|
||||
children.includes('@nx/') || command.includes('@nx/');
|
||||
function highlightChange(item: { label: string; value: string }) {
|
||||
push(asPath.split('#')[0] + '#' + item.value);
|
||||
onLineGroupSelectionChange?.(item.value);
|
||||
}
|
||||
return (
|
||||
<div className="my-8 w-full">
|
||||
<div className="code-block group relative w-full">
|
||||
<div>
|
||||
<div className="absolute top-0 right-0 z-10 flex">
|
||||
{enableCopy && enableCopy === true && (
|
||||
<CopyToClipboard
|
||||
text={command && command !== '' ? command : children}
|
||||
onCopy={() => {
|
||||
setCopied(true);
|
||||
}}
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
className={
|
||||
'opacity-0 transition-opacity group-hover:opacity-100 not-prose flex border border-slate-200 bg-slate-50/50 p-2 dark:border-slate-700 dark:bg-slate-800/60' +
|
||||
(highlightOptions && highlightOptions[0]
|
||||
? ''
|
||||
: ' rounded-tr-lg')
|
||||
}
|
||||
>
|
||||
{copied ? (
|
||||
<ClipboardDocumentCheckIcon className="h-5 w-5 text-blue-500 dark:text-sky-500" />
|
||||
) : (
|
||||
<ClipboardDocumentIcon className="h-5 w-5" />
|
||||
)}
|
||||
</button>
|
||||
</CopyToClipboard>
|
||||
)}
|
||||
{highlightOptions && highlightOptions[0] && (
|
||||
<Selector
|
||||
className="rounded-tr-lg"
|
||||
items={highlightOptions}
|
||||
selected={selectedOption}
|
||||
onChange={highlightChange}
|
||||
>
|
||||
<SparklesIcon className="h-5 w-5 mr-1"></SparklesIcon>
|
||||
</Selector>
|
||||
)}
|
||||
</div>
|
||||
<SyntaxHighlighter
|
||||
useInlineStyles={false}
|
||||
showLineNumbers={true}
|
||||
lineNumberStyle={lineNumberStyle}
|
||||
language={resolveLanguage(language)}
|
||||
children={children}
|
||||
PreTag={CodeWrapper({
|
||||
fileName,
|
||||
command,
|
||||
path,
|
||||
isMessageBelow: showRescopeMessage,
|
||||
language,
|
||||
children,
|
||||
})}
|
||||
/>
|
||||
{showRescopeMessage && (
|
||||
<a
|
||||
className="relative block rounded-b-md border border-slate-200 bg-slate-50 px-4 py-2 text-xs font-medium no-underline hover:underline dark:border-slate-700 dark:bg-slate-800"
|
||||
href="/recipes/other/rescope"
|
||||
title="Nx 16 package name changes"
|
||||
<div className="code-block group relative w-full">
|
||||
<div>
|
||||
<div className="absolute top-0 right-0 z-10 flex">
|
||||
{enableCopy && enableCopy === true && (
|
||||
<CopyToClipboard
|
||||
text={command && command !== '' ? command : children}
|
||||
onCopy={() => {
|
||||
setCopied(true);
|
||||
}}
|
||||
>
|
||||
<InformationCircleIcon
|
||||
className="mr-2 inline-block h-5 w-5"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
Nx 15 and lower use @nrwl/ instead of @nx/
|
||||
</a>
|
||||
<button
|
||||
type="button"
|
||||
className={
|
||||
'opacity-0 transition-opacity group-hover:opacity-100 not-prose flex border border-slate-200 bg-slate-50/50 p-2 dark:border-slate-700 dark:bg-slate-800/60' +
|
||||
(highlightOptions && highlightOptions[0]
|
||||
? ''
|
||||
: ' rounded-tr-lg')
|
||||
}
|
||||
>
|
||||
{copied ? (
|
||||
<ClipboardDocumentCheckIcon className="h-5 w-5 text-blue-500 dark:text-sky-500" />
|
||||
) : (
|
||||
<ClipboardDocumentIcon className="h-5 w-5" />
|
||||
)}
|
||||
</button>
|
||||
</CopyToClipboard>
|
||||
)}
|
||||
{highlightOptions && highlightOptions[0] && (
|
||||
<Selector
|
||||
className="rounded-tr-lg"
|
||||
items={highlightOptions}
|
||||
selected={selectedOption}
|
||||
onChange={highlightChange}
|
||||
>
|
||||
<SparklesIcon className="h-5 w-5 mr-1"></SparklesIcon>
|
||||
</Selector>
|
||||
)}
|
||||
</div>
|
||||
<SyntaxHighlighter
|
||||
useInlineStyles={false}
|
||||
showLineNumbers={true}
|
||||
lineNumberStyle={lineNumberStyle}
|
||||
language={resolveLanguage(language)}
|
||||
children={children}
|
||||
PreTag={CodeWrapper({
|
||||
fileName,
|
||||
command,
|
||||
path,
|
||||
isMessageBelow: showRescopeMessage,
|
||||
language,
|
||||
children,
|
||||
})}
|
||||
/>
|
||||
{showRescopeMessage && (
|
||||
<a
|
||||
className="relative block rounded-b-md border border-slate-200 bg-slate-50 px-4 py-2 text-xs font-medium no-underline hover:underline dark:border-slate-700 dark:bg-slate-800"
|
||||
href="/recipes/other/rescope"
|
||||
title="Nx 16 package name changes"
|
||||
>
|
||||
<InformationCircleIcon
|
||||
className="mr-2 inline-block h-5 w-5"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
Nx 15 and lower use @nrwl/ instead of @nx/
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
20
nx-dev/shared-ui-fence/tsconfig.json
Normal file
20
nx-dev/shared-ui-fence/tsconfig.json
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"jsx": "react-jsx",
|
||||
"allowJs": false,
|
||||
"esModuleInterop": false,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"strict": true
|
||||
},
|
||||
"files": [],
|
||||
"include": [],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.lib.json"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.spec.json"
|
||||
}
|
||||
],
|
||||
"extends": "../../tsconfig.base.json"
|
||||
}
|
||||
24
nx-dev/shared-ui-fence/tsconfig.lib.json
Normal file
24
nx-dev/shared-ui-fence/tsconfig.lib.json
Normal file
@ -0,0 +1,24 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../dist/out-tsc",
|
||||
"types": [
|
||||
"node",
|
||||
"@nx/react/typings/cssmodule.d.ts",
|
||||
"@nx/react/typings/image.d.ts"
|
||||
],
|
||||
"lib": ["DOM", "es2019"]
|
||||
},
|
||||
"exclude": [
|
||||
"jest.config.ts",
|
||||
"src/**/*.spec.ts",
|
||||
"src/**/*.test.ts",
|
||||
"src/**/*.spec.tsx",
|
||||
"src/**/*.test.tsx",
|
||||
"src/**/*.spec.js",
|
||||
"src/**/*.test.js",
|
||||
"src/**/*.spec.jsx",
|
||||
"src/**/*.test.jsx"
|
||||
],
|
||||
"include": ["src/**/*.js", "src/**/*.jsx", "src/**/*.ts", "src/**/*.tsx"]
|
||||
}
|
||||
20
nx-dev/shared-ui-fence/tsconfig.spec.json
Normal file
20
nx-dev/shared-ui-fence/tsconfig.spec.json
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../dist/out-tsc",
|
||||
"module": "commonjs",
|
||||
"types": ["jest", "node"]
|
||||
},
|
||||
"include": [
|
||||
"jest.config.ts",
|
||||
"src/**/*.test.ts",
|
||||
"src/**/*.spec.ts",
|
||||
"src/**/*.test.tsx",
|
||||
"src/**/*.spec.tsx",
|
||||
"src/**/*.test.js",
|
||||
"src/**/*.spec.js",
|
||||
"src/**/*.test.jsx",
|
||||
"src/**/*.spec.jsx",
|
||||
"src/**/*.d.ts"
|
||||
]
|
||||
}
|
||||
12
nx-dev/shared-ui-selector/.babelrc
Normal file
12
nx-dev/shared-ui-selector/.babelrc
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"presets": [
|
||||
[
|
||||
"@nx/react/babel",
|
||||
{
|
||||
"runtime": "automatic",
|
||||
"useBuiltIns": "usage"
|
||||
}
|
||||
]
|
||||
],
|
||||
"plugins": []
|
||||
}
|
||||
18
nx-dev/shared-ui-selector/.eslintrc.json
Normal file
18
nx-dev/shared-ui-selector/.eslintrc.json
Normal file
@ -0,0 +1,18 @@
|
||||
{
|
||||
"extends": ["plugin:@nx/react", "../../.eslintrc.json"],
|
||||
"ignorePatterns": ["!**/*"],
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
|
||||
"rules": {}
|
||||
},
|
||||
{
|
||||
"files": ["*.ts", "*.tsx"],
|
||||
"rules": {}
|
||||
},
|
||||
{
|
||||
"files": ["*.js", "*.jsx"],
|
||||
"rules": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
7
nx-dev/shared-ui-selector/README.md
Normal file
7
nx-dev/shared-ui-selector/README.md
Normal file
@ -0,0 +1,7 @@
|
||||
# shared-ui-selector
|
||||
|
||||
This library was generated with [Nx](https://nx.dev).
|
||||
|
||||
## Running unit tests
|
||||
|
||||
Run `nx test shared-ui-selector` to execute the unit tests via [Jest](https://jestjs.io).
|
||||
10
nx-dev/shared-ui-selector/jest.config.ts
Normal file
10
nx-dev/shared-ui-selector/jest.config.ts
Normal file
@ -0,0 +1,10 @@
|
||||
/* eslint-disable */
|
||||
export default {
|
||||
displayName: 'shared-ui-selector',
|
||||
preset: '../../jest.preset.js',
|
||||
transform: {
|
||||
'^.+\\.[tj]sx?$': ['babel-jest', { presets: ['@nx/react/babel'] }],
|
||||
},
|
||||
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
|
||||
coverageDirectory: '../../coverage/nx-dev/shared-ui-selector',
|
||||
};
|
||||
20
nx-dev/shared-ui-selector/project.json
Normal file
20
nx-dev/shared-ui-selector/project.json
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"name": "shared-ui-selector",
|
||||
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
||||
"sourceRoot": "nx-dev/shared-ui-selector/src",
|
||||
"projectType": "library",
|
||||
"tags": [],
|
||||
"targets": {
|
||||
"lint": {
|
||||
"executor": "@nx/eslint:lint"
|
||||
},
|
||||
"test": {
|
||||
"executor": "@nx/jest:jest",
|
||||
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
|
||||
"options": {
|
||||
"jestConfig": "nx-dev/shared-ui-selector/jest.config.ts",
|
||||
"passWithNoTests": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
1
nx-dev/shared-ui-selector/src/index.ts
Normal file
1
nx-dev/shared-ui-selector/src/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './lib/selector';
|
||||
20
nx-dev/shared-ui-selector/tsconfig.json
Normal file
20
nx-dev/shared-ui-selector/tsconfig.json
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"jsx": "react-jsx",
|
||||
"allowJs": false,
|
||||
"esModuleInterop": false,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"strict": true
|
||||
},
|
||||
"files": [],
|
||||
"include": [],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.lib.json"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.spec.json"
|
||||
}
|
||||
],
|
||||
"extends": "../../tsconfig.base.json"
|
||||
}
|
||||
24
nx-dev/shared-ui-selector/tsconfig.lib.json
Normal file
24
nx-dev/shared-ui-selector/tsconfig.lib.json
Normal file
@ -0,0 +1,24 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../dist/out-tsc",
|
||||
"types": [
|
||||
"node",
|
||||
|
||||
"@nx/react/typings/cssmodule.d.ts",
|
||||
"@nx/react/typings/image.d.ts"
|
||||
]
|
||||
},
|
||||
"exclude": [
|
||||
"jest.config.ts",
|
||||
"src/**/*.spec.ts",
|
||||
"src/**/*.test.ts",
|
||||
"src/**/*.spec.tsx",
|
||||
"src/**/*.test.tsx",
|
||||
"src/**/*.spec.js",
|
||||
"src/**/*.test.js",
|
||||
"src/**/*.spec.jsx",
|
||||
"src/**/*.test.jsx"
|
||||
],
|
||||
"include": ["src/**/*.js", "src/**/*.jsx", "src/**/*.ts", "src/**/*.tsx"]
|
||||
}
|
||||
20
nx-dev/shared-ui-selector/tsconfig.spec.json
Normal file
20
nx-dev/shared-ui-selector/tsconfig.spec.json
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../dist/out-tsc",
|
||||
"module": "commonjs",
|
||||
"types": ["jest", "node"]
|
||||
},
|
||||
"include": [
|
||||
"jest.config.ts",
|
||||
"src/**/*.test.ts",
|
||||
"src/**/*.spec.ts",
|
||||
"src/**/*.test.tsx",
|
||||
"src/**/*.spec.tsx",
|
||||
"src/**/*.test.js",
|
||||
"src/**/*.spec.js",
|
||||
"src/**/*.test.jsx",
|
||||
"src/**/*.spec.jsx",
|
||||
"src/**/*.d.ts"
|
||||
]
|
||||
}
|
||||
@ -1,6 +1,7 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
// @ts-ignore
|
||||
import { CopyToClipboard } from 'react-copy-to-clipboard';
|
||||
// @ts-ignore
|
||||
import SyntaxHighlighter from 'react-syntax-highlighter';
|
||||
|
||||
/* eslint-disable-next-line */
|
||||
|
||||
@ -7,7 +7,6 @@ export * from './lib/champion-perks';
|
||||
export * from './lib/header';
|
||||
export * from './lib/flip-card';
|
||||
export * from './lib/footer';
|
||||
export * from './lib/selector';
|
||||
export * from './lib/sidebar-container';
|
||||
export * from './lib/sidebar';
|
||||
export * from './lib/nx-users-showcase';
|
||||
|
||||
@ -8,8 +8,6 @@ import {
|
||||
} from '@markdoc/markdoc';
|
||||
import { load as yamlLoad } from 'js-yaml';
|
||||
import React, { ReactNode } from 'react';
|
||||
import { Fence } from './lib/nodes/fence.component';
|
||||
import { fence } from './lib/nodes/fence.schema';
|
||||
import { Heading } from './lib/nodes/heading.component';
|
||||
import { heading } from './lib/nodes/heading.schema';
|
||||
import { getImageSchema } from './lib/nodes/image.schema';
|
||||
@ -55,6 +53,8 @@ import { VideoLink, videoLink } from './lib/tags/video-link.component';
|
||||
import { Pill } from './lib/tags/pill.component';
|
||||
import { pill } from './lib/tags/pill.schema';
|
||||
import { frameworkIcons } from './lib/icons';
|
||||
import { fence } from './lib/nodes/fence.schema';
|
||||
import { FenceWrapper } from './lib/nodes/fence-wrapper.component';
|
||||
|
||||
// TODO fix this export
|
||||
export { GithubRepository } from './lib/tags/github-repository.component';
|
||||
@ -103,7 +103,7 @@ export const getMarkdocCustomConfig = (
|
||||
Disclosure,
|
||||
LinkCard,
|
||||
CustomLink,
|
||||
Fence,
|
||||
FenceWrapper,
|
||||
GithubRepository,
|
||||
StackblitzButton,
|
||||
Graph,
|
||||
|
||||
47
nx-dev/ui-markdoc/src/lib/nodes/fence-wrapper.component.tsx
Normal file
47
nx-dev/ui-markdoc/src/lib/nodes/fence-wrapper.component.tsx
Normal file
@ -0,0 +1,47 @@
|
||||
import { Fence, FenceProps } from '@nx/shared-ui-fence';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
const useUrlHash = (initialValue: string) => {
|
||||
const router = useRouter();
|
||||
const [hash, setHash] = useState(initialValue);
|
||||
|
||||
const updateHash = (str: string) => {
|
||||
if (!str) return;
|
||||
setHash(str.split('#')[1]);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const onWindowHashChange = () => updateHash(window.location.hash);
|
||||
const onNextJSHashChange = (url: string) => updateHash(url);
|
||||
|
||||
router.events.on('hashChangeStart', onNextJSHashChange);
|
||||
window.addEventListener('hashchange', onWindowHashChange);
|
||||
window.addEventListener('load', onWindowHashChange);
|
||||
return () => {
|
||||
router.events.off('hashChangeStart', onNextJSHashChange);
|
||||
window.removeEventListener('load', onWindowHashChange);
|
||||
window.removeEventListener('hashchange', onWindowHashChange);
|
||||
};
|
||||
}, [router.asPath, router.events]);
|
||||
|
||||
return hash;
|
||||
};
|
||||
|
||||
export function FenceWrapper(props: FenceProps) {
|
||||
const { push, asPath } = useRouter();
|
||||
const hash = decodeURIComponent(useUrlHash(''));
|
||||
|
||||
const modifiedProps: FenceProps = {
|
||||
...props,
|
||||
selectedLineGroup: hash,
|
||||
onLineGroupSelectionChange: (selection: string) => {
|
||||
push(asPath.split('#')[0] + '#' + selection);
|
||||
},
|
||||
};
|
||||
return (
|
||||
<div className="my-8 w-full">
|
||||
<Fence {...modifiedProps} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -1,7 +1,7 @@
|
||||
import { Schema, Tag } from '@markdoc/markdoc';
|
||||
|
||||
export const fence: Schema = {
|
||||
render: 'Fence',
|
||||
render: 'FenceWrapper',
|
||||
attributes: {
|
||||
content: { type: 'String', render: false, required: true },
|
||||
language: { type: 'String' },
|
||||
@ -18,6 +18,6 @@ export const fence: Schema = {
|
||||
const children = node.children.length
|
||||
? node.transformChildren(config)
|
||||
: [node.attributes['content']];
|
||||
return new Tag('Fence', attributes, children);
|
||||
return new Tag('FenceWrapper', attributes, children);
|
||||
},
|
||||
};
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { TerminalShellWrapper } from '@nx/shared-ui-fence';
|
||||
import { VideoLoop } from './video-loop.component';
|
||||
import { Schema } from '@markdoc/markdoc';
|
||||
import { TerminalShellWrapper } from '../nodes/fences/terminal-shell.component';
|
||||
|
||||
export const terminalVideo: Schema = {
|
||||
render: 'TerminalVideo',
|
||||
|
||||
15
package.json
15
package.json
@ -106,7 +106,7 @@
|
||||
"@swc/cli": "0.1.62",
|
||||
"@swc/core": "^1.3.85",
|
||||
"@swc/jest": "^0.2.20",
|
||||
"@testing-library/react": "13.4.0",
|
||||
"@testing-library/react": "14.0.0",
|
||||
"@types/cytoscape": "^3.18.2",
|
||||
"@types/detect-port": "^1.3.2",
|
||||
"@types/ejs": "3.1.2",
|
||||
@ -123,8 +123,8 @@
|
||||
"@types/node": "18.16.9",
|
||||
"@types/npm-package-arg": "6.1.1",
|
||||
"@types/prettier": "^2.6.2",
|
||||
"@types/react": "18.2.24",
|
||||
"@types/react-dom": "18.2.9",
|
||||
"@types/react": "18.2.33",
|
||||
"@types/react-dom": "18.2.14",
|
||||
"@types/semver": "^7.5.2",
|
||||
"@types/tar-stream": "^2.2.2",
|
||||
"@types/tmp": "^0.2.0",
|
||||
@ -169,10 +169,10 @@
|
||||
"eslint-config-next": "13.1.1",
|
||||
"eslint-config-prettier": "9.0.0",
|
||||
"eslint-plugin-cypress": "2.14.0",
|
||||
"eslint-plugin-import": "2.26.0",
|
||||
"eslint-plugin-jsx-a11y": "6.6.1",
|
||||
"eslint-plugin-import": "2.27.5",
|
||||
"eslint-plugin-jsx-a11y": "6.7.1",
|
||||
"eslint-plugin-playwright": "^0.15.3",
|
||||
"eslint-plugin-react": "7.31.11",
|
||||
"eslint-plugin-react": "7.32.2",
|
||||
"eslint-plugin-react-hooks": "4.6.0",
|
||||
"eslint-plugin-storybook": "^0.6.12",
|
||||
"express": "^4.18.1",
|
||||
@ -249,7 +249,7 @@
|
||||
"react-markdown": "^8.0.7",
|
||||
"react-redux": "8.0.5",
|
||||
"react-refresh": "^0.10.0",
|
||||
"react-router-dom": "^6.11.2",
|
||||
"react-router-dom": "^6.21.2",
|
||||
"react-textarea-autosize": "^8.5.3",
|
||||
"regenerator-runtime": "0.13.7",
|
||||
"resolve.exports": "1.1.0",
|
||||
@ -371,4 +371,3 @@
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -9,7 +9,12 @@ describe('createWatchPaths', () => {
|
||||
const testDir = joinPathFragments(workspaceRoot, 'e2e/remix');
|
||||
|
||||
const paths = await createWatchPaths(testDir);
|
||||
expect(paths).toEqual(['../../packages', '../../graph', '../../e2e/utils']);
|
||||
expect(paths).toEqual([
|
||||
'../../packages',
|
||||
'../../graph',
|
||||
'../../nx-dev',
|
||||
'../../e2e/utils',
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
1849
pnpm-lock.yaml
generated
1849
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@ -102,6 +102,8 @@
|
||||
"@nx/remix/*": ["packages/remix/*"],
|
||||
"@nx/rollup": ["packages/rollup"],
|
||||
"@nx/rollup/*": ["packages/rollup/*"],
|
||||
"@nx/shared-ui-fence": ["nx-dev/shared-ui-fence/src/index.ts"],
|
||||
"@nx/shared-ui-selector": ["nx-dev/shared-ui-selector/src/index.ts"],
|
||||
"@nx/storybook": ["packages/storybook"],
|
||||
"@nx/storybook/*": ["packages/storybook/*"],
|
||||
"@nx/typedoc-theme": ["typedoc-theme/src/index.ts"],
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user