More work on the visualiser
This commit is contained in:
parent
64c601d8c8
commit
5852d1f67a
@ -89,6 +89,24 @@ const VisualiserTab: React.FC<VisualiserTabProps> = ({
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If multiple predecessors, position task at the average of their columns
|
||||||
|
if (predecessors.length > 1) {
|
||||||
|
const predColumns = predecessors
|
||||||
|
.map((predGuid) => {
|
||||||
|
const predTask = byGuid.get(predGuid);
|
||||||
|
return predTask ? getColumn(predTask) : 0;
|
||||||
|
})
|
||||||
|
.filter((col) => col !== undefined);
|
||||||
|
|
||||||
|
if (predColumns.length > 0) {
|
||||||
|
const avgColumn =
|
||||||
|
predColumns.reduce((sum, col) => sum + col, 0) / predColumns.length;
|
||||||
|
const column = Math.round(avgColumn);
|
||||||
|
columnByGuid.set(guid, column);
|
||||||
|
return column;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Get predecessor column and find which child index this is
|
// Get predecessor column and find which child index this is
|
||||||
const predTask = byGuid.get(predecessors[0]);
|
const predTask = byGuid.get(predecessors[0]);
|
||||||
if (!predTask) {
|
if (!predTask) {
|
||||||
@ -231,19 +249,130 @@ const VisualiserTab: React.FC<VisualiserTabProps> = ({
|
|||||||
if (numColumns <= 1) return [50];
|
if (numColumns <= 1) return [50];
|
||||||
|
|
||||||
return levelTasks.map((task) => {
|
return levelTasks.map((task) => {
|
||||||
|
const predecessors = (task.config.predecessors as string[]) ?? [];
|
||||||
const taskColumn =
|
const taskColumn =
|
||||||
taskColumnMap.current.get(task.config.guid as string) ?? 0;
|
taskColumnMap.current.get(task.config.guid as string) ?? 0;
|
||||||
|
|
||||||
|
if (predecessors.length > 1) {
|
||||||
|
const predColumns = predecessors
|
||||||
|
.map((predGuid) => taskColumnMap.current.get(predGuid))
|
||||||
|
.filter((col) => col !== undefined) as number[];
|
||||||
|
|
||||||
|
if (predColumns.length > 0) {
|
||||||
|
const avgPredColumn =
|
||||||
|
predColumns.reduce((sum, col) => sum + col, 0) / predColumns.length;
|
||||||
|
const gridCol = avgPredColumn - columnRange.min;
|
||||||
|
// Map averaged column to percentage for centered merges
|
||||||
|
return ((gridCol + 0.5) / numColumns) * 100;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const gridCol = taskColumn - columnRange.min;
|
const gridCol = taskColumn - columnRange.min;
|
||||||
// Map grid column to percentage (e.g., 2 columns: col 0 = 25%, col 1 = 75%)
|
// Map grid column to percentage (e.g., 2 columns: col 0 = 25%, col 1 = 75%)
|
||||||
return ((gridCol + 0.5) / numColumns) * 100;
|
return ((gridCol + 0.5) / numColumns) * 100;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getPredecessorPositionsByColumn = (
|
||||||
|
levelTasks: CreateWorkflowTemplateVersion["tasks"][],
|
||||||
|
): number[] => {
|
||||||
|
const numColumns = columnRange.max - columnRange.min + 1;
|
||||||
|
if (numColumns <= 1) return [50];
|
||||||
|
|
||||||
|
const predColumns = new Set<number>();
|
||||||
|
|
||||||
|
levelTasks.forEach((task) => {
|
||||||
|
const preds = (task.config.predecessors as string[]) ?? [];
|
||||||
|
preds.forEach((predGuid) => {
|
||||||
|
const predColumn = taskColumnMap.current.get(predGuid);
|
||||||
|
if (predColumn !== undefined) {
|
||||||
|
predColumns.add(predColumn);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return Array.from(predColumns).map((predColumn) => {
|
||||||
|
const gridCol = predColumn - columnRange.min;
|
||||||
|
return ((gridCol + 0.5) / numColumns) * 100;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const renderConnector = (
|
const renderConnector = (
|
||||||
count: number,
|
count: number,
|
||||||
positions?: number[],
|
positions?: number[],
|
||||||
predecessorPositions?: number[],
|
predecessorPositions?: number[],
|
||||||
|
currentLevelTasks?: CreateWorkflowTemplateVersion["tasks"][],
|
||||||
) => {
|
) => {
|
||||||
|
// Check if any task in this level has multiple predecessors from different columns
|
||||||
|
const hasMultiplePredecessorColumns =
|
||||||
|
currentLevelTasks &&
|
||||||
|
currentLevelTasks.some((task) => {
|
||||||
|
const preds = (task.config.predecessors as string[]) ?? [];
|
||||||
|
if (preds.length <= 1) return false;
|
||||||
|
|
||||||
|
const predColumns = preds
|
||||||
|
.map((predGuid) => taskColumnMap.current.get(predGuid))
|
||||||
|
.filter((col) => col !== undefined) as number[];
|
||||||
|
|
||||||
|
return new Set(predColumns).size > 1; // Multiple different columns
|
||||||
|
});
|
||||||
|
|
||||||
|
// If we have a merging scenario (multiple predecessors converging)
|
||||||
|
if (
|
||||||
|
hasMultiplePredecessorColumns &&
|
||||||
|
predecessorPositions &&
|
||||||
|
predecessorPositions.length > 1 &&
|
||||||
|
positions
|
||||||
|
) {
|
||||||
|
const minPredX = Math.min(...predecessorPositions);
|
||||||
|
const maxPredX = Math.max(...predecessorPositions);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
className="visualiser-connector-branch"
|
||||||
|
viewBox="0 0 100 40"
|
||||||
|
preserveAspectRatio="none"
|
||||||
|
>
|
||||||
|
{/* Vertical lines from each predecessor */}
|
||||||
|
{predecessorPositions.map((x, idx) => (
|
||||||
|
<line
|
||||||
|
key={`pred-${idx}`}
|
||||||
|
className="visualiser-connector-branch-line"
|
||||||
|
x1={x}
|
||||||
|
y1="0"
|
||||||
|
x2={x}
|
||||||
|
y2="14"
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
{/* Horizontal line connecting all predecessors */}
|
||||||
|
<line
|
||||||
|
className="visualiser-connector-branch-line"
|
||||||
|
x1={minPredX}
|
||||||
|
y1="14"
|
||||||
|
x2={maxPredX}
|
||||||
|
y2="14"
|
||||||
|
/>
|
||||||
|
{/* Vertical lines down to children */}
|
||||||
|
{positions.map((x, idx) => (
|
||||||
|
<g key={`child-${idx}`}>
|
||||||
|
<line
|
||||||
|
className="visualiser-connector-branch-line"
|
||||||
|
x1={x}
|
||||||
|
y1="14"
|
||||||
|
x2={x}
|
||||||
|
y2="28"
|
||||||
|
/>
|
||||||
|
<polygon
|
||||||
|
className="visualiser-connector-branch-arrow"
|
||||||
|
points={`${x - 1.5},28 ${x + 1.5},28 ${x},34`}
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
))}
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (count <= 1) {
|
if (count <= 1) {
|
||||||
// For a single child, use predecessor position if available
|
// For a single child, use predecessor position if available
|
||||||
let startX = 50;
|
let startX = 50;
|
||||||
@ -431,7 +560,8 @@ const VisualiserTab: React.FC<VisualiserTabProps> = ({
|
|||||||
renderConnector(
|
renderConnector(
|
||||||
levels[0].length,
|
levels[0].length,
|
||||||
getConnectorPositionsByColumn(levels[0]),
|
getConnectorPositionsByColumn(levels[0]),
|
||||||
undefined,
|
getPredecessorPositionsByColumn(levels[0]),
|
||||||
|
levels[0],
|
||||||
)}
|
)}
|
||||||
{levels.map((level, index) => (
|
{levels.map((level, index) => (
|
||||||
<React.Fragment key={`level-${index}`}>
|
<React.Fragment key={`level-${index}`}>
|
||||||
@ -461,6 +591,10 @@ const VisualiserTab: React.FC<VisualiserTabProps> = ({
|
|||||||
)?.length;
|
)?.length;
|
||||||
const numColumns = columnRange.max - columnRange.min + 1;
|
const numColumns = columnRange.max - columnRange.min + 1;
|
||||||
|
|
||||||
|
const predecessors = task.config.predecessors as
|
||||||
|
| string[]
|
||||||
|
| undefined;
|
||||||
|
|
||||||
let styleObj: React.CSSProperties = {};
|
let styleObj: React.CSSProperties = {};
|
||||||
|
|
||||||
if (isRootTask && numColumns > 1) {
|
if (isRootTask && numColumns > 1) {
|
||||||
@ -470,6 +604,28 @@ const VisualiserTab: React.FC<VisualiserTabProps> = ({
|
|||||||
display: "flex",
|
display: "flex",
|
||||||
justifyContent: "center",
|
justifyContent: "center",
|
||||||
};
|
};
|
||||||
|
} else if (predecessors && predecessors.length > 1) {
|
||||||
|
// Tasks with multiple predecessors - span across predecessor columns
|
||||||
|
const predColumns = predecessors
|
||||||
|
.map((predGuid) => taskColumnMap.current.get(predGuid))
|
||||||
|
.filter((col) => col !== undefined) as number[];
|
||||||
|
|
||||||
|
if (predColumns.length > 1) {
|
||||||
|
const minPredCol = Math.min(...predColumns);
|
||||||
|
const maxPredCol = Math.max(...predColumns);
|
||||||
|
const startGridCol = minPredCol - columnRange.min + 1;
|
||||||
|
const endGridCol = maxPredCol - columnRange.min + 2;
|
||||||
|
|
||||||
|
styleObj = {
|
||||||
|
gridColumn: `${startGridCol} / ${endGridCol}`,
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "center",
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
// Fallback to normal positioning
|
||||||
|
const gridColumn = taskColumn - columnRange.min + 1;
|
||||||
|
styleObj = { gridColumn };
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// Child tasks positioned in their specific column
|
// Child tasks positioned in their specific column
|
||||||
const gridColumn = taskColumn - columnRange.min + 1;
|
const gridColumn = taskColumn - columnRange.min + 1;
|
||||||
@ -503,12 +659,14 @@ const VisualiserTab: React.FC<VisualiserTabProps> = ({
|
|||||||
? renderConnector(
|
? renderConnector(
|
||||||
levels[index + 1].length,
|
levels[index + 1].length,
|
||||||
getConnectorPositionsByColumn(levels[index + 1]),
|
getConnectorPositionsByColumn(levels[index + 1]),
|
||||||
getConnectorPositionsByColumn(levels[index]),
|
getPredecessorPositionsByColumn(levels[index + 1]),
|
||||||
|
levels[index + 1],
|
||||||
)
|
)
|
||||||
: renderConnector(1, undefined, undefined)}
|
: renderConnector(1, undefined, undefined, undefined)}
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
))}
|
))}
|
||||||
{levels.length === 0 && renderConnector(1)}
|
{levels.length === 0 &&
|
||||||
|
renderConnector(1, undefined, undefined, undefined)}
|
||||||
<div className="visualiser-node">
|
<div className="visualiser-node">
|
||||||
<div className="visualiser-node-content">
|
<div className="visualiser-node-content">
|
||||||
<span>End</span>
|
<span>End</span>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user