mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-09-19 15:30:03 +02:00
made draw commit more readable, included more helper functions and interfaces, added in-source test suite to renderer
This commit is contained in:
@@ -9,6 +9,9 @@ import type { GitGraphDiagramConfig } from '../../config.type.js';
|
||||
|
||||
let allCommitsDict = new Map();
|
||||
|
||||
const LAYOUT_OFFSET = 10;
|
||||
const COMMIT_STEP = 40;
|
||||
|
||||
const commitType: CommitType = {
|
||||
NORMAL: 0,
|
||||
REVERSE: 1,
|
||||
@@ -29,6 +32,10 @@ interface CommitPosition {
|
||||
y: number;
|
||||
}
|
||||
|
||||
interface CommitPositionOffset extends CommitPosition {
|
||||
posWithOffset: number;
|
||||
}
|
||||
|
||||
const branchPos = new Map<string, BranchPosition>();
|
||||
const commitPos = new Map<string, CommitPosition>();
|
||||
let lanes: number[] = [];
|
||||
@@ -93,9 +100,7 @@ const findClosestParent = (parents: string[], useBTLogic = false): string | unde
|
||||
const setParallelBTPos = (
|
||||
sortedKeys: string[],
|
||||
commits: Map<string, Commit>,
|
||||
defaultPos: number,
|
||||
commitStep: number,
|
||||
layoutOffset: number
|
||||
defaultPos: number
|
||||
) => {
|
||||
let curPos = defaultPos;
|
||||
let maxPosition = defaultPos;
|
||||
@@ -108,12 +113,12 @@ const setParallelBTPos = (
|
||||
}
|
||||
|
||||
if (hasParents(commit)) {
|
||||
curPos = calculateCommitPosition(commit, commitStep, maxPosition);
|
||||
curPos = calculateCommitPosition(commit);
|
||||
maxPosition = Math.max(curPos, maxPosition);
|
||||
} else {
|
||||
roots.push(commit);
|
||||
}
|
||||
setCommitPosition(commit, curPos, layoutOffset);
|
||||
setCommitPosition(commit, curPos);
|
||||
});
|
||||
|
||||
curPos = maxPosition;
|
||||
@@ -125,7 +130,7 @@ const setParallelBTPos = (
|
||||
const hasParents = (commit: Commit): boolean => commit.parents?.length > 0;
|
||||
|
||||
const findClosestParentPos = (commit: Commit): number => {
|
||||
const closestParent = findClosestParent(commit.parents.filter((p) => p !== null) as string[]);
|
||||
const closestParent = findClosestParent(commit.parents.filter((p) => p !== null));
|
||||
if (!closestParent) {
|
||||
throw new Error(`Closest parent not found for commit ${commit.id}`);
|
||||
}
|
||||
@@ -137,19 +142,19 @@ const findClosestParentPos = (commit: Commit): number => {
|
||||
return closestParentPos;
|
||||
};
|
||||
|
||||
const calculateCommitPosition = (commit: Commit, commitStep: number): number => {
|
||||
const calculateCommitPosition = (commit: Commit): number => {
|
||||
const closestParentPos = findClosestParentPos(commit);
|
||||
return closestParentPos + commitStep;
|
||||
return closestParentPos + COMMIT_STEP;
|
||||
};
|
||||
|
||||
const setCommitPosition = (commit: Commit, curPos: number, layoutOffset: number) => {
|
||||
const setCommitPosition = (commit: Commit, curPos: number) => {
|
||||
const branch = branchPos.get(commit.branch);
|
||||
if (!branch) {
|
||||
throw new Error(`Branch not found for commit ${commit.id}`);
|
||||
}
|
||||
|
||||
const x = branch.pos;
|
||||
const y = curPos + layoutOffset;
|
||||
const y = curPos + LAYOUT_OFFSET;
|
||||
commitPos.set(commit.id, { x, y });
|
||||
};
|
||||
|
||||
@@ -167,8 +172,7 @@ const setRootPosition = (commit: Commit, curPos: number, defaultPos: number) =>
|
||||
const drawCommitBullet = (
|
||||
gBullets: d3.Selection<SVGGElement, unknown, HTMLElement, any>,
|
||||
commit: Commit,
|
||||
x: number,
|
||||
y: number,
|
||||
commitPosition: CommitPositionOffset,
|
||||
typeClass: string,
|
||||
branchIndex: number,
|
||||
commitSymbolType: number
|
||||
@@ -176,8 +180,8 @@ const drawCommitBullet = (
|
||||
if (commitSymbolType === commitType.HIGHLIGHT) {
|
||||
gBullets
|
||||
.append('rect')
|
||||
.attr('x', x - 10)
|
||||
.attr('y', y - 10)
|
||||
.attr('x', commitPosition.x - 10)
|
||||
.attr('y', commitPosition.y - 10)
|
||||
.attr('width', 20)
|
||||
.attr('height', 20)
|
||||
.attr(
|
||||
@@ -186,8 +190,8 @@ const drawCommitBullet = (
|
||||
);
|
||||
gBullets
|
||||
.append('rect')
|
||||
.attr('x', x - 6)
|
||||
.attr('y', y - 6)
|
||||
.attr('x', commitPosition.x - 6)
|
||||
.attr('y', commitPosition.y - 6)
|
||||
.attr('width', 12)
|
||||
.attr('height', 12)
|
||||
.attr(
|
||||
@@ -197,50 +201,50 @@ const drawCommitBullet = (
|
||||
} else if (commitSymbolType === commitType.CHERRY_PICK) {
|
||||
gBullets
|
||||
.append('circle')
|
||||
.attr('cx', x)
|
||||
.attr('cy', y)
|
||||
.attr('cx', commitPosition.x)
|
||||
.attr('cy', commitPosition.y)
|
||||
.attr('r', 10)
|
||||
.attr('class', `commit ${commit.id} ${typeClass}`);
|
||||
gBullets
|
||||
.append('circle')
|
||||
.attr('cx', x - 3)
|
||||
.attr('cy', y + 2)
|
||||
.attr('cx', commitPosition.x - 3)
|
||||
.attr('cy', commitPosition.y + 2)
|
||||
.attr('r', 2.75)
|
||||
.attr('fill', '#fff')
|
||||
.attr('class', `commit ${commit.id} ${typeClass}`);
|
||||
gBullets
|
||||
.append('circle')
|
||||
.attr('cx', x + 3)
|
||||
.attr('cy', y + 2)
|
||||
.attr('cx', commitPosition.x + 3)
|
||||
.attr('cy', commitPosition.y + 2)
|
||||
.attr('r', 2.75)
|
||||
.attr('fill', '#fff')
|
||||
.attr('class', `commit ${commit.id} ${typeClass}`);
|
||||
gBullets
|
||||
.append('line')
|
||||
.attr('x1', x + 3)
|
||||
.attr('y1', y + 1)
|
||||
.attr('x2', x)
|
||||
.attr('y2', y - 5)
|
||||
.attr('x1', commitPosition.x + 3)
|
||||
.attr('y1', commitPosition.y + 1)
|
||||
.attr('x2', commitPosition.x)
|
||||
.attr('y2', commitPosition.y - 5)
|
||||
.attr('stroke', '#fff')
|
||||
.attr('class', `commit ${commit.id} ${typeClass}`);
|
||||
gBullets
|
||||
.append('line')
|
||||
.attr('x1', x - 3)
|
||||
.attr('y1', y + 1)
|
||||
.attr('x2', x)
|
||||
.attr('y2', y - 5)
|
||||
.attr('x1', commitPosition.x - 3)
|
||||
.attr('y1', commitPosition.y + 1)
|
||||
.attr('x2', commitPosition.x)
|
||||
.attr('y2', commitPosition.y - 5)
|
||||
.attr('stroke', '#fff')
|
||||
.attr('class', `commit ${commit.id} ${typeClass}`);
|
||||
} else {
|
||||
const circle = gBullets.append('circle');
|
||||
circle.attr('cx', x);
|
||||
circle.attr('cy', y);
|
||||
circle.attr('cx', commitPosition.x);
|
||||
circle.attr('cy', commitPosition.y);
|
||||
circle.attr('r', commit.type === commitType.MERGE ? 9 : 10);
|
||||
circle.attr('class', `commit ${commit.id} commit${branchIndex % THEME_COLOR_LIMIT}`);
|
||||
if (commit.type === commitType.MERGE) {
|
||||
const circle2 = gBullets.append('circle');
|
||||
circle2.attr('cx', x);
|
||||
circle2.attr('cy', y);
|
||||
circle2.attr('cx', commitPosition.x);
|
||||
circle2.attr('cy', commitPosition.y);
|
||||
circle2.attr('r', 6);
|
||||
circle2.attr(
|
||||
'class',
|
||||
@@ -250,7 +254,10 @@ const drawCommitBullet = (
|
||||
if (commitSymbolType === commitType.REVERSE) {
|
||||
const cross = gBullets.append('path');
|
||||
cross
|
||||
.attr('d', `M ${x - 5},${y - 5}L${x + 5},${y + 5}M${x - 5},${y + 5}L${x + 5},${y - 5}`)
|
||||
.attr(
|
||||
'd',
|
||||
`M ${commitPosition.x - 5},${commitPosition.y - 5}L${commitPosition.x + 5},${commitPosition.y + 5}M${commitPosition.x - 5},${commitPosition.y + 5}L${commitPosition.x + 5},${commitPosition.y - 5}`
|
||||
)
|
||||
.attr('class', `commit ${typeClass} ${commit.id} commit${branchIndex % THEME_COLOR_LIMIT}`);
|
||||
}
|
||||
}
|
||||
@@ -259,10 +266,8 @@ const drawCommitBullet = (
|
||||
const drawCommitLabel = (
|
||||
gLabels: d3.Selection<SVGGElement, unknown, HTMLElement, any>,
|
||||
commit: Commit,
|
||||
x: number,
|
||||
y: number,
|
||||
commitPosition: CommitPositionOffset,
|
||||
pos: number,
|
||||
posWithOffset: number,
|
||||
gitGraphConfig: GitGraphDiagramConfig
|
||||
) => {
|
||||
if (
|
||||
@@ -274,34 +279,52 @@ const drawCommitLabel = (
|
||||
const labelBkg = wrapper.insert('rect').attr('class', 'commit-label-bkg');
|
||||
const text = wrapper
|
||||
.append('text')
|
||||
.attr('x', x)
|
||||
.attr('y', y + 25)
|
||||
.attr('x', commitPosition.x)
|
||||
.attr('y', commitPosition.y + 25)
|
||||
.attr('class', 'commit-label')
|
||||
.text(commit.id);
|
||||
const bbox = text.node()?.getBBox();
|
||||
|
||||
if (bbox) {
|
||||
labelBkg
|
||||
.attr('x', posWithOffset - bbox.width / 2 - 2)
|
||||
.attr('y', y + 13.5)
|
||||
.attr('x', commitPosition.posWithOffset - bbox.width / 2 - 2)
|
||||
.attr('y', commitPosition.y + 13.5)
|
||||
.attr('width', bbox.width + 4)
|
||||
.attr('height', bbox.height + 4);
|
||||
|
||||
if (dir === 'TB' || dir === 'BT') {
|
||||
labelBkg.attr('x', x - (bbox.width + 4)).attr('y', y - 12);
|
||||
text.attr('x', x - (bbox.width + 2)).attr('y', y + bbox.height - 12);
|
||||
labelBkg.attr('x', commitPosition.x - (bbox.width + 4)).attr('y', commitPosition.y - 12);
|
||||
text
|
||||
.attr('x', commitPosition.x - (bbox.width + 2))
|
||||
.attr('y', commitPosition.y + bbox.height - 12);
|
||||
}
|
||||
|
||||
if (gitGraphConfig.rotateCommitLabel) {
|
||||
if (dir === 'TB' || dir === 'BT') {
|
||||
text.attr('transform', 'rotate(' + -45 + ', ' + x + ', ' + y + ')');
|
||||
labelBkg.attr('transform', 'rotate(' + -45 + ', ' + x + ', ' + y + ')');
|
||||
text.attr(
|
||||
'transform',
|
||||
'rotate(' + -45 + ', ' + commitPosition.x + ', ' + commitPosition.y + ')'
|
||||
);
|
||||
labelBkg.attr(
|
||||
'transform',
|
||||
'rotate(' + -45 + ', ' + commitPosition.x + ', ' + commitPosition.y + ')'
|
||||
);
|
||||
} else {
|
||||
const r_x = -7.5 - ((bbox.width + 10) / 25) * 9.5;
|
||||
const r_y = 10 + (bbox.width / 25) * 8.5;
|
||||
wrapper.attr(
|
||||
'transform',
|
||||
'translate(' + r_x + ', ' + r_y + ') rotate(' + -45 + ', ' + pos + ', ' + y + ')'
|
||||
'translate(' +
|
||||
r_x +
|
||||
', ' +
|
||||
r_y +
|
||||
') rotate(' +
|
||||
-45 +
|
||||
', ' +
|
||||
pos +
|
||||
', ' +
|
||||
commitPosition.y +
|
||||
')'
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -312,11 +335,8 @@ const drawCommitLabel = (
|
||||
const drawCommitTags = (
|
||||
gLabels: d3.Selection<SVGGElement, unknown, HTMLElement, any>,
|
||||
commit: Commit,
|
||||
x: number,
|
||||
y: number,
|
||||
pos: number,
|
||||
posWithOffset: number,
|
||||
layoutOffset: number
|
||||
commitPosition: CommitPositionOffset,
|
||||
pos: number
|
||||
) => {
|
||||
if (commit.tags.length > 0) {
|
||||
let yOffset = 0;
|
||||
@@ -329,7 +349,7 @@ const drawCommitTags = (
|
||||
const hole = gLabels.append('circle');
|
||||
const tag = gLabels
|
||||
.append('text')
|
||||
.attr('y', y - 16 - yOffset)
|
||||
.attr('y', commitPosition.y - 16 - yOffset)
|
||||
.attr('class', 'tag-label')
|
||||
.text(tagValue);
|
||||
const tagBbox = tag.node()?.getBBox();
|
||||
@@ -339,7 +359,7 @@ const drawCommitTags = (
|
||||
maxTagBboxWidth = Math.max(maxTagBboxWidth, tagBbox.width);
|
||||
maxTagBboxHeight = Math.max(maxTagBboxHeight, tagBbox.height);
|
||||
|
||||
tag.attr('x', posWithOffset - tagBbox.width / 2);
|
||||
tag.attr('x', commitPosition.posWithOffset - tagBbox.width / 2);
|
||||
|
||||
tagElements.push({
|
||||
tag,
|
||||
@@ -353,16 +373,16 @@ const drawCommitTags = (
|
||||
|
||||
for (const { tag, hole, rect, yOffset } of tagElements) {
|
||||
const h2 = maxTagBboxHeight / 2;
|
||||
const ly = y - 19.2 - yOffset;
|
||||
const ly = commitPosition.y - 19.2 - yOffset;
|
||||
rect.attr('class', 'tag-label-bkg').attr(
|
||||
'points',
|
||||
`
|
||||
${pos - maxTagBboxWidth / 2 - 2},${ly + 2}
|
||||
${pos - maxTagBboxWidth / 2 - 2},${ly - 2}
|
||||
${posWithOffset - maxTagBboxWidth / 2 - 4},${ly - h2 - 2}
|
||||
${posWithOffset + maxTagBboxWidth / 2 + 4},${ly - h2 - 2}
|
||||
${posWithOffset + maxTagBboxWidth / 2 + 4},${ly + h2 + 2}
|
||||
${posWithOffset - maxTagBboxWidth / 2 - 4},${ly + h2 + 2}`
|
||||
${commitPosition.posWithOffset - maxTagBboxWidth / 2 - 4},${ly - h2 - 2}
|
||||
${commitPosition.posWithOffset + maxTagBboxWidth / 2 + 4},${ly - h2 - 2}
|
||||
${commitPosition.posWithOffset + maxTagBboxWidth / 2 + 4},${ly + h2 + 2}
|
||||
${commitPosition.posWithOffset - maxTagBboxWidth / 2 - 4},${ly + h2 + 2}`
|
||||
);
|
||||
|
||||
hole
|
||||
@@ -379,22 +399,22 @@ const drawCommitTags = (
|
||||
.attr(
|
||||
'points',
|
||||
`
|
||||
${x},${yOrigin + 2}
|
||||
${x},${yOrigin - 2}
|
||||
${x + layoutOffset},${yOrigin - h2 - 2}
|
||||
${x + layoutOffset + maxTagBboxWidth + 4},${yOrigin - h2 - 2}
|
||||
${x + layoutOffset + maxTagBboxWidth + 4},${yOrigin + h2 + 2}
|
||||
${x + layoutOffset},${yOrigin + h2 + 2}`
|
||||
${commitPosition.x},${yOrigin + 2}
|
||||
${commitPosition.x},${yOrigin - 2}
|
||||
${commitPosition.x + LAYOUT_OFFSET},${yOrigin - h2 - 2}
|
||||
${commitPosition.x + LAYOUT_OFFSET + maxTagBboxWidth + 4},${yOrigin - h2 - 2}
|
||||
${commitPosition.x + LAYOUT_OFFSET + maxTagBboxWidth + 4},${yOrigin + h2 + 2}
|
||||
${commitPosition.x + LAYOUT_OFFSET},${yOrigin + h2 + 2}`
|
||||
)
|
||||
.attr('transform', 'translate(12,12) rotate(45, ' + x + ',' + pos + ')');
|
||||
.attr('transform', 'translate(12,12) rotate(45, ' + commitPosition.x + ',' + pos + ')');
|
||||
hole
|
||||
.attr('cx', x + 2)
|
||||
.attr('cx', commitPosition.x + 2)
|
||||
.attr('cy', yOrigin)
|
||||
.attr('transform', 'translate(12,12) rotate(45, ' + x + ',' + pos + ')');
|
||||
.attr('transform', 'translate(12,12) rotate(45, ' + commitPosition.x + ',' + pos + ')');
|
||||
tag
|
||||
.attr('x', x + 5)
|
||||
.attr('x', commitPosition.x + 5)
|
||||
.attr('y', yOrigin + 3)
|
||||
.attr('transform', 'translate(14,14) rotate(45, ' + x + ',' + pos + ')');
|
||||
.attr('transform', 'translate(14,14) rotate(45, ' + commitPosition.x + ',' + pos + ')');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -421,30 +441,23 @@ const getCommitClassType = (commit: Commit): string => {
|
||||
const calculatePosition = (
|
||||
commit: Commit,
|
||||
dir: string,
|
||||
isParallelCommits: boolean,
|
||||
pos: number,
|
||||
commitStep: number,
|
||||
layoutOffset: number,
|
||||
commitPos: Map<string, CommitPosition>
|
||||
): number => {
|
||||
const defaultCommitPosition = { x: 0, y: defaultPos }; // Default position if commit is not found
|
||||
const defaultCommitPosition = { x: 0, y: 0 }; // Default position if commit is not found
|
||||
|
||||
if (isParallelCommits) {
|
||||
if (commit.parents.length > 0) {
|
||||
const closestParent =
|
||||
dir === 'BT' ? findClosestParent(commit.parents) : findClosestParent(commit.parents);
|
||||
|
||||
// Check if closestParent is defined
|
||||
const closestParent = findClosestParent(commit.parents);
|
||||
if (closestParent) {
|
||||
const parentPosition = commitPos.get(closestParent) ?? defaultCommitPosition;
|
||||
|
||||
if (dir === 'TB') {
|
||||
return parentPosition.y + commitStep;
|
||||
return parentPosition.y + COMMIT_STEP;
|
||||
} else if (dir === 'BT') {
|
||||
const currentPosition = commitPos.get(commit.id) ?? defaultCommitPosition;
|
||||
return currentPosition.y - commitStep;
|
||||
return currentPosition.y - COMMIT_STEP;
|
||||
} else {
|
||||
return parentPosition.x + commitStep;
|
||||
return parentPosition.x + COMMIT_STEP;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -452,14 +465,26 @@ const calculatePosition = (
|
||||
return defaultPos;
|
||||
} else if (dir === 'BT') {
|
||||
const currentPosition = commitPos.get(commit.id) ?? defaultCommitPosition;
|
||||
return currentPosition.y - commitStep;
|
||||
return currentPosition.y - COMMIT_STEP;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
|
||||
return dir === 'TB' && isParallelCommits ? pos : pos + layoutOffset;
|
||||
const getCommitPosition = (
|
||||
commit: Commit,
|
||||
pos: number,
|
||||
isParallelCommits: boolean
|
||||
): CommitPositionOffset => {
|
||||
const posWithOffset = dir === 'BT' && isParallelCommits ? pos : pos + LAYOUT_OFFSET;
|
||||
const y = dir === 'TB' || dir === 'BT' ? posWithOffset : branchPos.get(commit.branch)?.pos;
|
||||
const x = dir === 'TB' || dir === 'BT' ? branchPos.get(commit.branch)?.pos : posWithOffset;
|
||||
if (x === undefined || y === undefined) {
|
||||
throw new Error(`Position were undefined for commit ${commit.id}`);
|
||||
}
|
||||
return { x, y, posWithOffset };
|
||||
};
|
||||
|
||||
const drawCommits = (
|
||||
@@ -476,8 +501,6 @@ const drawCommits = (
|
||||
let pos = dir === 'TB' || dir === 'BT' ? defaultPos : 0;
|
||||
const keys = [...commits.keys()];
|
||||
const isParallelCommits = gitGraphConfig?.parallelCommits ?? false;
|
||||
const layoutOffset = 10;
|
||||
const commitStep = 40;
|
||||
|
||||
const sortKeys = (a: string, b: string) => {
|
||||
const seqA = commits.get(a)?.seq;
|
||||
@@ -487,12 +510,10 @@ const drawCommits = (
|
||||
|
||||
let sortedKeys = keys.sort(sortKeys);
|
||||
|
||||
if (dir === 'BT' && !isParallelCommits) {
|
||||
sortedKeys = sortedKeys.reverse();
|
||||
if (dir === 'BT') {
|
||||
if (isParallelCommits) {
|
||||
setParallelBTPos(sortedKeys, commits, pos);
|
||||
}
|
||||
|
||||
if (dir === 'BT' && isParallelCommits) {
|
||||
setParallelBTPos(sortedKeys, commits, pos, commitStep, layoutOffset);
|
||||
sortedKeys = sortedKeys.reverse();
|
||||
}
|
||||
|
||||
@@ -500,41 +521,30 @@ const drawCommits = (
|
||||
const commit = commits.get(key);
|
||||
if (!commit) {
|
||||
throw new Error(`Commit not found for key ${key}`);
|
||||
} else {
|
||||
pos = calculatePosition(
|
||||
commit,
|
||||
dir,
|
||||
isParallelCommits,
|
||||
pos,
|
||||
commitStep,
|
||||
layoutOffset,
|
||||
commitPos
|
||||
);
|
||||
const posWithOffset = dir === 'BT' && isParallelCommits ? pos : pos + layoutOffset;
|
||||
const y = dir === 'TB' || dir === 'BT' ? posWithOffset : branchPos.get(commit.branch)?.pos;
|
||||
const x = dir === 'TB' || dir === 'BT' ? branchPos.get(commit.branch)?.pos : posWithOffset;
|
||||
if (x === undefined || y === undefined) {
|
||||
throw new Error(`Position were undefined for commit ${commit.id}`);
|
||||
}
|
||||
if (isParallelCommits) {
|
||||
pos = calculatePosition(commit, dir, pos, commitPos);
|
||||
}
|
||||
|
||||
const commitPosition = getCommitPosition(commit, pos, isParallelCommits);
|
||||
// Don't draw the commits now but calculate the positioning which is used by the branch lines etc.
|
||||
if (modifyGraph) {
|
||||
const typeClass = getCommitClassType(commit);
|
||||
const commitSymbolType = commit.customType ?? commit.type;
|
||||
const branchIndex = branchPos.get(commit.branch)?.index ?? 0;
|
||||
drawCommitBullet(gBullets, commit, x, y, typeClass, branchIndex, commitSymbolType);
|
||||
drawCommitLabel(gLabels, commit, x, y, pos, posWithOffset, gitGraphConfig);
|
||||
drawCommitTags(gLabels, commit, x, y, pos, posWithOffset, layoutOffset);
|
||||
drawCommitBullet(gBullets, commit, commitPosition, typeClass, branchIndex, commitSymbolType);
|
||||
drawCommitLabel(gLabels, commit, commitPosition, pos, gitGraphConfig);
|
||||
drawCommitTags(gLabels, commit, commitPosition, pos);
|
||||
}
|
||||
if (dir === 'TB' || dir === 'BT') {
|
||||
commitPos.set(commit.id, { x: x, y: posWithOffset });
|
||||
commitPos.set(commit.id, { x: commitPosition.x, y: commitPosition.posWithOffset });
|
||||
} else {
|
||||
commitPos.set(commit.id, { x: posWithOffset, y: y });
|
||||
commitPos.set(commit.id, { x: commitPosition.posWithOffset, y: commitPosition.y });
|
||||
}
|
||||
pos = dir === 'BT' && isParallelCommits ? pos + commitStep : pos + commitStep + layoutOffset;
|
||||
pos = dir === 'BT' && isParallelCommits ? pos + COMMIT_STEP : pos + COMMIT_STEP + LAYOUT_OFFSET;
|
||||
if (pos > maxPos) {
|
||||
maxPos = pos;
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@@ -904,65 +914,269 @@ const drawBranches = (
|
||||
});
|
||||
};
|
||||
|
||||
const setBranchPosition = function (
|
||||
name: string,
|
||||
pos: number,
|
||||
index: number,
|
||||
bbox: DOMRect,
|
||||
rotateCommitLabel: boolean
|
||||
): number {
|
||||
branchPos.set(name, { pos, index });
|
||||
pos += 50 + (rotateCommitLabel ? 40 : 0) + (dir === 'TB' || dir === 'BT' ? bbox.width / 2 : 0);
|
||||
return pos;
|
||||
};
|
||||
|
||||
export const draw: DrawDefinition = function (txt, id, ver, diagObj) {
|
||||
clear();
|
||||
const conf = getConfig();
|
||||
const gitGraphConfig = conf.gitGraph;
|
||||
// try {
|
||||
|
||||
log.debug('in gitgraph renderer', txt + '\n', 'id:', id, ver);
|
||||
if (!gitGraphConfig) {
|
||||
throw new Error('GitGraph config not found');
|
||||
}
|
||||
const rotateCommitLabel = gitGraphConfig.rotateCommitLabel ?? false;
|
||||
const db = diagObj.db as GitGraphDB;
|
||||
allCommitsDict = db.getCommits();
|
||||
const branches = db.getBranchesAsObjArray();
|
||||
dir = db.getDirection();
|
||||
const diagram = select(`[id="${id}"]`);
|
||||
// Position branches
|
||||
let pos = 0;
|
||||
|
||||
branches.forEach((branch, index) => {
|
||||
const labelElement = drawText(branch.name);
|
||||
const g = diagram.append('g');
|
||||
const branchLabel = g.insert('g').attr('class', 'branchLabel');
|
||||
const label = branchLabel.insert('g').attr('class', 'label branch-label');
|
||||
// @ts-ignore: TODO Fix ts errors
|
||||
label.node().appendChild(labelElement);
|
||||
const bbox = labelElement.getBBox();
|
||||
|
||||
branchPos.set(branch.name, { pos, index });
|
||||
pos +=
|
||||
50 +
|
||||
// @ts-ignore: TODO Fix ts errors
|
||||
(gitGraphConfig.rotateCommitLabel ? 40 : 0) +
|
||||
(dir === 'TB' || dir === 'BT' ? bbox.width / 2 : 0);
|
||||
pos = setBranchPosition(branch.name, pos, index, bbox, rotateCommitLabel);
|
||||
label.remove();
|
||||
branchLabel.remove();
|
||||
g.remove();
|
||||
});
|
||||
|
||||
drawCommits(diagram, allCommitsDict, false);
|
||||
// @ts-ignore: TODO Fix ts errors
|
||||
if (gitGraphConfig.showBranches) {
|
||||
drawBranches(diagram, branches);
|
||||
}
|
||||
drawArrows(diagram, allCommitsDict);
|
||||
drawCommits(diagram, allCommitsDict, true);
|
||||
|
||||
utils.insertTitle(
|
||||
diagram,
|
||||
'gitTitleText',
|
||||
// @ts-ignore: TODO Fix ts errors
|
||||
gitGraphConfig.titleTopMargin,
|
||||
gitGraphConfig.titleTopMargin ?? 0,
|
||||
db.getDiagramTitle()
|
||||
);
|
||||
|
||||
// Setup the view box and size of the svg element
|
||||
setupGraphViewbox(
|
||||
undefined,
|
||||
diagram,
|
||||
// @ts-ignore: TODO Fix ts errors
|
||||
gitGraphConfig.diagramPadding,
|
||||
// @ts-ignore: TODO Fix ts errors
|
||||
gitGraphConfig.useMaxWidth ?? conf.useMaxWidth
|
||||
);
|
||||
setupGraphViewbox(undefined, diagram, gitGraphConfig.diagramPadding, gitGraphConfig.useMaxWidth);
|
||||
};
|
||||
|
||||
export default {
|
||||
draw,
|
||||
};
|
||||
|
||||
if (import.meta.vitest) {
|
||||
const { it, expect, describe } = import.meta.vitest;
|
||||
|
||||
describe('drawText', () => {
|
||||
it('should drawText', () => {
|
||||
const svgLabel = drawText('main');
|
||||
expect(svgLabel).toBeDefined();
|
||||
expect(svgLabel.children[0].innerHTML).toBe('main');
|
||||
});
|
||||
});
|
||||
|
||||
describe('drawBranchPositions', () => {
|
||||
const bbox: DOMRect = {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 10,
|
||||
height: 10,
|
||||
top: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
toJSON: () => '',
|
||||
};
|
||||
|
||||
it('should setBranchPositions LR with two branches', () => {
|
||||
dir = 'LR';
|
||||
|
||||
const pos = setBranchPosition('main', 0, 0, bbox, true);
|
||||
expect(pos).toBe(90);
|
||||
expect(branchPos.get('main')).toEqual({ pos: 0, index: 0 });
|
||||
const posNext = setBranchPosition('develop', pos, 1, bbox, true);
|
||||
expect(posNext).toBe(180);
|
||||
expect(branchPos.get('develop')).toEqual({ pos: pos, index: 1 });
|
||||
});
|
||||
|
||||
it('should setBranchPositions TB with two branches', () => {
|
||||
dir = 'TB';
|
||||
bbox.width = 34.9921875;
|
||||
|
||||
const pos = setBranchPosition('main', 0, 0, bbox, true);
|
||||
expect(pos).toBe(107.49609375);
|
||||
expect(branchPos.get('main')).toEqual({ pos: 0, index: 0 });
|
||||
|
||||
bbox.width = 56.421875;
|
||||
const posNext = setBranchPosition('develop', pos, 1, bbox, true);
|
||||
expect(posNext).toBe(225.70703125);
|
||||
expect(branchPos.get('develop')).toEqual({ pos: pos, index: 1 });
|
||||
});
|
||||
});
|
||||
/*
|
||||
describe('drawCommits', () => {
|
||||
dir = 'TB';
|
||||
const commits = new Map<string, Commit>([
|
||||
[
|
||||
'commitZero',
|
||||
{
|
||||
id: 'ZERO',
|
||||
message: '',
|
||||
seq: 0,
|
||||
type: commitType.NORMAL,
|
||||
tags: [],
|
||||
parents: [],
|
||||
branch: 'main',
|
||||
},
|
||||
],
|
||||
[
|
||||
'commitA',
|
||||
{
|
||||
id: 'A',
|
||||
message: '',
|
||||
seq: 1,
|
||||
type: commitType.NORMAL,
|
||||
tags: [],
|
||||
parents: ['ZERO'],
|
||||
branch: 'feature',
|
||||
},
|
||||
],
|
||||
[
|
||||
'commitB',
|
||||
{
|
||||
id: 'B',
|
||||
message: '',
|
||||
seq: 2,
|
||||
type: commitType.NORMAL,
|
||||
tags: [],
|
||||
parents: ['A'],
|
||||
branch: 'feature',
|
||||
},
|
||||
],
|
||||
[
|
||||
'commitM',
|
||||
{
|
||||
id: 'M',
|
||||
message: 'merged branch feature into main',
|
||||
seq: 3,
|
||||
type: commitType.MERGE,
|
||||
tags: [],
|
||||
parents: ['ZERO', 'B'],
|
||||
branch: 'main',
|
||||
customId: true,
|
||||
},
|
||||
],
|
||||
[
|
||||
'commitC',
|
||||
{
|
||||
id: 'C',
|
||||
message: '',
|
||||
seq: 4,
|
||||
type: commitType.NORMAL,
|
||||
tags: [],
|
||||
parents: ['ZERO'],
|
||||
branch: 'release',
|
||||
},
|
||||
],
|
||||
[
|
||||
'commit5_8928ea0',
|
||||
{
|
||||
id: '5-8928ea0',
|
||||
message: 'cherry-picked [object Object] into release',
|
||||
seq: 5,
|
||||
type: commitType.CHERRY_PICK,
|
||||
tags: [],
|
||||
parents: ['C', 'M'],
|
||||
branch: 'release',
|
||||
},
|
||||
],
|
||||
[
|
||||
'commitD',
|
||||
{
|
||||
id: 'D',
|
||||
message: '',
|
||||
seq: 6,
|
||||
type: commitType.NORMAL,
|
||||
tags: [],
|
||||
parents: ['5-8928ea0'],
|
||||
branch: 'release',
|
||||
},
|
||||
],
|
||||
[
|
||||
'commit7_ed848ba',
|
||||
{
|
||||
id: '7-ed848ba',
|
||||
message: 'cherry-picked [object Object] into release',
|
||||
seq: 7,
|
||||
type: commitType.CHERRY_PICK,
|
||||
tags: [],
|
||||
parents: ['D', 'M'],
|
||||
branch: 'release',
|
||||
},
|
||||
],
|
||||
]);
|
||||
|
||||
branchPos.set('main', { pos: 0, index: 0 });
|
||||
branchPos.set('feature', { pos: 107.49609375, index: 1 });
|
||||
branchPos.set('release', { pos: 224.03515625, index: 2 });
|
||||
|
||||
commits.forEach((commit) => {
|
||||
it(`should draw commit ${commit.id}`, () => {
|
||||
const commitPosition = getCommitPosition(commit, 0, false);
|
||||
expect(commitPosition).toBeDefined();
|
||||
});
|
||||
it(`should draw commit ${commit.id} with position`, () => {
|
||||
const commitPosition = getCommitPosition(commit, 0, false);
|
||||
expect(commitPosition.x).toBeDefined();
|
||||
expect(commitPosition.y).toBeDefined();
|
||||
expect(commitPosition.posWithOffset).toBeDefined();
|
||||
}
|
||||
it(`should draw commit ${commit.id} bullet`, () => {
|
||||
const gBullets = svg.append('g').attr('class', 'commit-bullets');
|
||||
const typeClass = getCommitClassType(commit);
|
||||
const branchIndex = branchPos.get(commit.branch)?.index ?? 0;
|
||||
drawCommitBullet(gBullets, commit, commitPosition, typeClass, branchIndex, commit.type);
|
||||
}
|
||||
it(`should draw commit ${commit.id} label`, () => {
|
||||
const gLabels = svg.append('g').attr('class', 'commit-labels');
|
||||
drawCommitLabel(gLabels, commit, commitPosition, 0, gitGraphConfig);
|
||||
}
|
||||
});
|
||||
*/
|
||||
describe('drawBranches', () => {
|
||||
it('should drawBranches', () => {
|
||||
expect(true).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('drawArrows', () => {
|
||||
it('should drawArrows', () => {
|
||||
expect(true).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
it('add', () => {
|
||||
commitPos.set('parent1', { x: 1, y: 1 });
|
||||
commitPos.set('parent2', { x: 2, y: 2 });
|
||||
commitPos.set('parent3', { x: 3, y: 3 });
|
||||
dir = 'LR';
|
||||
const parents = ['parent1', 'parent2', 'parent3'];
|
||||
const closestParent = findClosestParent(parents);
|
||||
|
||||
expect(closestParent).toBe('parent3');
|
||||
commitPos.clear();
|
||||
});
|
||||
}
|
||||
|
Reference in New Issue
Block a user