Looking even better
This commit is contained in:
parent
b1f502f8d7
commit
e867e530b4
@ -85,8 +85,25 @@ const VisualiserTab: React.FC<VisualiserTabProps> = ({
|
|||||||
|
|
||||||
const predecessors = task.config.predecessors as string[] | undefined;
|
const predecessors = task.config.predecessors as string[] | undefined;
|
||||||
if (!predecessors || predecessors.length === 0) {
|
if (!predecessors || predecessors.length === 0) {
|
||||||
columnByGuid.set(guid, 0);
|
// Spread root tasks across columns when there are multiple starts
|
||||||
return 0;
|
const rootTasks = tasks.filter((t) => {
|
||||||
|
const preds = t.config.predecessors as string[] | undefined;
|
||||||
|
return !preds || preds.length === 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
rootTasks.sort((a, b) => {
|
||||||
|
const nameA = (a.config.name as string) || a.type || "";
|
||||||
|
const nameB = (b.config.name as string) || b.type || "";
|
||||||
|
return nameA.localeCompare(nameB);
|
||||||
|
});
|
||||||
|
|
||||||
|
const rootIndex = rootTasks.findIndex(
|
||||||
|
(t) => (t.config.guid as string) === guid,
|
||||||
|
);
|
||||||
|
|
||||||
|
const column = Math.max(0, rootIndex);
|
||||||
|
columnByGuid.set(guid, column);
|
||||||
|
return column;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If multiple predecessors, position task at the average of their columns
|
// If multiple predecessors, position task at the average of their columns
|
||||||
@ -241,6 +258,13 @@ const VisualiserTab: React.FC<VisualiserTabProps> = ({
|
|||||||
return found?.positions ?? [];
|
return found?.positions ?? [];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getColumnPercent = (column: number): number => {
|
||||||
|
const numColumns = columnRange.max - columnRange.min + 1;
|
||||||
|
if (numColumns <= 1) return 50;
|
||||||
|
const gridCol = column - columnRange.min;
|
||||||
|
return ((gridCol + 0.5) / numColumns) * 100;
|
||||||
|
};
|
||||||
|
|
||||||
// Calculate connector positions based on grid columns
|
// Calculate connector positions based on grid columns
|
||||||
const getConnectorPositionsByColumn = (
|
const getConnectorPositionsByColumn = (
|
||||||
levelTasks: CreateWorkflowTemplateVersion["tasks"][],
|
levelTasks: CreateWorkflowTemplateVersion["tasks"][],
|
||||||
@ -275,6 +299,7 @@ const VisualiserTab: React.FC<VisualiserTabProps> = ({
|
|||||||
|
|
||||||
const getPredecessorPositionsByColumn = (
|
const getPredecessorPositionsByColumn = (
|
||||||
levelTasks: CreateWorkflowTemplateVersion["tasks"][],
|
levelTasks: CreateWorkflowTemplateVersion["tasks"][],
|
||||||
|
excludeTasks?: Set<string>,
|
||||||
): number[] => {
|
): number[] => {
|
||||||
const numColumns = columnRange.max - columnRange.min + 1;
|
const numColumns = columnRange.max - columnRange.min + 1;
|
||||||
if (numColumns <= 1) return [50];
|
if (numColumns <= 1) return [50];
|
||||||
@ -284,6 +309,9 @@ const VisualiserTab: React.FC<VisualiserTabProps> = ({
|
|||||||
levelTasks.forEach((task) => {
|
levelTasks.forEach((task) => {
|
||||||
const preds = (task.config.predecessors as string[]) ?? [];
|
const preds = (task.config.predecessors as string[]) ?? [];
|
||||||
preds.forEach((predGuid) => {
|
preds.forEach((predGuid) => {
|
||||||
|
// Skip excluded tasks (e.g., those with override)
|
||||||
|
if (excludeTasks && excludeTasks.has(predGuid)) return;
|
||||||
|
|
||||||
const predColumn = taskColumnMap.current.get(predGuid);
|
const predColumn = taskColumnMap.current.get(predGuid);
|
||||||
if (predColumn !== undefined) {
|
if (predColumn !== undefined) {
|
||||||
predColumns.add(predColumn);
|
predColumns.add(predColumn);
|
||||||
@ -291,10 +319,9 @@ const VisualiserTab: React.FC<VisualiserTabProps> = ({
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
return Array.from(predColumns).map((predColumn) => {
|
return Array.from(predColumns).map((predColumn) =>
|
||||||
const gridCol = predColumn - columnRange.min;
|
getColumnPercent(predColumn),
|
||||||
return ((gridCol + 0.5) / numColumns) * 100;
|
);
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderConnector = (
|
const renderConnector = (
|
||||||
@ -402,7 +429,7 @@ const VisualiserTab: React.FC<VisualiserTabProps> = ({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise render SVG with line from start to end (always, even if vertical)
|
// Render orthogonal connector (vertical-horizontal-vertical)
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
@ -410,12 +437,31 @@ const VisualiserTab: React.FC<VisualiserTabProps> = ({
|
|||||||
viewBox="0 0 100 40"
|
viewBox="0 0 100 40"
|
||||||
preserveAspectRatio="none"
|
preserveAspectRatio="none"
|
||||||
>
|
>
|
||||||
|
{/* Vertical line from predecessor */}
|
||||||
<line
|
<line
|
||||||
className="visualiser-connector-branch-line"
|
className="visualiser-connector-branch-line"
|
||||||
x1={startX}
|
x1={startX}
|
||||||
y1="0"
|
y1="0"
|
||||||
|
x2={startX}
|
||||||
|
y2="14"
|
||||||
|
/>
|
||||||
|
{/* Horizontal line connecting to child column */}
|
||||||
|
{startX !== endX && (
|
||||||
|
<line
|
||||||
|
className="visualiser-connector-branch-line"
|
||||||
|
x1={startX}
|
||||||
|
y1="14"
|
||||||
x2={endX}
|
x2={endX}
|
||||||
y2="40"
|
y2="14"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{/* Vertical line down to child */}
|
||||||
|
<line
|
||||||
|
className="visualiser-connector-branch-line"
|
||||||
|
x1={endX}
|
||||||
|
y1="14"
|
||||||
|
x2={endX}
|
||||||
|
y2="34"
|
||||||
/>
|
/>
|
||||||
<polygon
|
<polygon
|
||||||
className="visualiser-connector-branch-arrow"
|
className="visualiser-connector-branch-arrow"
|
||||||
@ -457,6 +503,11 @@ const VisualiserTab: React.FC<VisualiserTabProps> = ({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const topX =
|
||||||
|
predecessorPositions && predecessorPositions.length > 0
|
||||||
|
? predecessorPositions[0]
|
||||||
|
: 50;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
@ -466,9 +517,9 @@ const VisualiserTab: React.FC<VisualiserTabProps> = ({
|
|||||||
>
|
>
|
||||||
<line
|
<line
|
||||||
className="visualiser-connector-branch-line"
|
className="visualiser-connector-branch-line"
|
||||||
x1="50"
|
x1={topX}
|
||||||
y1="0"
|
y1="0"
|
||||||
x2="50"
|
x2={topX}
|
||||||
y2="14"
|
y2="14"
|
||||||
/>
|
/>
|
||||||
<line
|
<line
|
||||||
@ -547,6 +598,11 @@ const VisualiserTab: React.FC<VisualiserTabProps> = ({
|
|||||||
return connections;
|
return connections;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const rootTaskCount = tasks.filter((task) => {
|
||||||
|
const preds = task.config.predecessors as string[] | undefined;
|
||||||
|
return !preds || preds.length === 0;
|
||||||
|
}).length;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="visualiser-root">
|
<div className="visualiser-root">
|
||||||
<div className="visualiser-container" ref={visualiserContainerRef}>
|
<div className="visualiser-container" ref={visualiserContainerRef}>
|
||||||
@ -585,7 +641,7 @@ const VisualiserTab: React.FC<VisualiserTabProps> = ({
|
|||||||
|
|
||||||
let styleObj: React.CSSProperties = {};
|
let styleObj: React.CSSProperties = {};
|
||||||
|
|
||||||
if (isRootTask && numColumns > 1) {
|
if (isRootTask && numColumns > 1 && rootTaskCount === 1) {
|
||||||
// Root tasks span all columns and center their content
|
// Root tasks span all columns and center their content
|
||||||
styleObj = {
|
styleObj = {
|
||||||
gridColumn: `1 / -1`,
|
gridColumn: `1 / -1`,
|
||||||
@ -644,12 +700,89 @@ const VisualiserTab: React.FC<VisualiserTabProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{index < levels.length - 1
|
{index < levels.length - 1
|
||||||
? renderConnector(
|
? (() => {
|
||||||
levels[index + 1].length,
|
const currentLevel = levels[index];
|
||||||
getConnectorPositionsByColumn(levels[index + 1]),
|
const nextLevel = levels[index + 1];
|
||||||
getPredecessorPositionsByColumn(levels[index + 1]),
|
|
||||||
levels[index + 1],
|
// Tasks with override should not have normal progression connectors
|
||||||
|
const overrideTasks = new Set(
|
||||||
|
currentLevel
|
||||||
|
.filter(
|
||||||
|
(task) =>
|
||||||
|
task.config.overrideDefaultTaskProgression === true,
|
||||||
)
|
)
|
||||||
|
.map((task) => task.config.guid as string),
|
||||||
|
);
|
||||||
|
|
||||||
|
const hasMergeTask = nextLevel.some((task) => {
|
||||||
|
const preds =
|
||||||
|
(task.config.predecessors as string[]) ?? [];
|
||||||
|
// Only consider predecessors without override
|
||||||
|
const validPreds = preds.filter(
|
||||||
|
(predGuid) => !overrideTasks.has(predGuid),
|
||||||
|
);
|
||||||
|
if (validPreds.length <= 1) return false;
|
||||||
|
|
||||||
|
const predColumns = validPreds
|
||||||
|
.map((predGuid) => taskColumnMap.current.get(predGuid))
|
||||||
|
.filter((col) => col !== undefined) as number[];
|
||||||
|
|
||||||
|
return new Set(predColumns).size > 1;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (hasMergeTask) {
|
||||||
|
// Filter to tasks with at least one valid predecessor
|
||||||
|
const tasksWithValidPreds = nextLevel.filter((task) => {
|
||||||
|
const preds =
|
||||||
|
(task.config.predecessors as string[]) ?? [];
|
||||||
|
return preds.some(
|
||||||
|
(predGuid) => !overrideTasks.has(predGuid),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
return renderConnector(
|
||||||
|
tasksWithValidPreds.length,
|
||||||
|
getConnectorPositionsByColumn(tasksWithValidPreds),
|
||||||
|
getPredecessorPositionsByColumn(
|
||||||
|
tasksWithValidPreds,
|
||||||
|
overrideTasks,
|
||||||
|
),
|
||||||
|
tasksWithValidPreds,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const groups = new Map<
|
||||||
|
string,
|
||||||
|
CreateWorkflowTemplateVersion["tasks"][]
|
||||||
|
>();
|
||||||
|
|
||||||
|
nextLevel.forEach((task) => {
|
||||||
|
const preds =
|
||||||
|
(task.config.predecessors as string[]) ?? [];
|
||||||
|
preds.forEach((predGuid) => {
|
||||||
|
// Skip predecessors with override
|
||||||
|
if (overrideTasks.has(predGuid)) return;
|
||||||
|
|
||||||
|
const list = groups.get(predGuid) ?? [];
|
||||||
|
list.push(task);
|
||||||
|
groups.set(predGuid, list);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return Array.from(groups.entries()).map(
|
||||||
|
([predGuid, children]) => {
|
||||||
|
const predColumn = taskColumnMap.current.get(predGuid);
|
||||||
|
if (predColumn === undefined) return null;
|
||||||
|
|
||||||
|
return renderConnector(
|
||||||
|
children.length,
|
||||||
|
getConnectorPositionsByColumn(children),
|
||||||
|
[getColumnPercent(predColumn)],
|
||||||
|
children,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
})()
|
||||||
: null}
|
: null}
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
))}
|
))}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user