Render the git graph on the server (#12333)
Rendering the git graph on the server means that we can properly track flows and switch from the Canvas implementation to a SVG implementation. * This implementation provides a 16 limited color selection * The uniqued color numbers are also provided * And there is also a monochrome version *In addition is a hover highlight that allows users to highlight commits on the same flow. Closes #12209 Signed-off-by: Andrew Thornton art27@cantab.net Co-authored-by: silverwind <me@silverwind.io>
This commit is contained in:
parent
f1a42f5d5e
commit
2c1ae6c82d
19 changed files with 1666 additions and 696 deletions
|
@ -1,568 +1,81 @@
|
|||
// Although inspired by the https://github.com/bluef/gitgraph.js/blob/master/gitgraph.js
|
||||
// this has been completely rewritten with almost no remaining code
|
||||
|
||||
// GitGraphCanvas is a canvas for drawing gitgraphs on to
|
||||
class GitGraphCanvas {
|
||||
constructor(canvas, widthUnits, heightUnits, config) {
|
||||
this.ctx = canvas.getContext('2d');
|
||||
|
||||
const width = widthUnits * config.unitSize;
|
||||
this.height = heightUnits * config.unitSize;
|
||||
|
||||
const ratio = window.devicePixelRatio || 1;
|
||||
|
||||
canvas.width = width * ratio;
|
||||
canvas.height = this.height * ratio;
|
||||
|
||||
canvas.style.width = `${width}px`;
|
||||
canvas.style.height = `${this.height}px`;
|
||||
|
||||
this.ctx.lineWidth = config.lineWidth;
|
||||
this.ctx.lineJoin = 'round';
|
||||
this.ctx.lineCap = 'round';
|
||||
|
||||
this.ctx.scale(ratio, ratio);
|
||||
this.config = config;
|
||||
}
|
||||
drawLine(moveX, moveY, lineX, lineY, color) {
|
||||
this.ctx.strokeStyle = color;
|
||||
this.ctx.beginPath();
|
||||
this.ctx.moveTo(moveX, moveY);
|
||||
this.ctx.lineTo(lineX, lineY);
|
||||
this.ctx.stroke();
|
||||
}
|
||||
drawLineRight(x, y, color) {
|
||||
this.drawLine(
|
||||
x - 0.5 * this.config.unitSize,
|
||||
y + this.config.unitSize / 2,
|
||||
x + 0.5 * this.config.unitSize,
|
||||
y + this.config.unitSize / 2,
|
||||
color
|
||||
);
|
||||
}
|
||||
drawLineUp(x, y, color) {
|
||||
this.drawLine(
|
||||
x,
|
||||
y + this.config.unitSize / 2,
|
||||
x,
|
||||
y - this.config.unitSize / 2,
|
||||
color
|
||||
);
|
||||
}
|
||||
drawNode(x, y, color) {
|
||||
this.ctx.strokeStyle = color;
|
||||
|
||||
this.drawLineUp(x, y, color);
|
||||
|
||||
this.ctx.beginPath();
|
||||
this.ctx.arc(x, y, this.config.nodeRadius, 0, Math.PI * 2, true);
|
||||
this.ctx.fillStyle = color;
|
||||
this.ctx.fill();
|
||||
}
|
||||
drawLineIn(x, y, color) {
|
||||
this.drawLine(
|
||||
x + 0.5 * this.config.unitSize,
|
||||
y + this.config.unitSize / 2,
|
||||
x - 0.5 * this.config.unitSize,
|
||||
y - this.config.unitSize / 2,
|
||||
color
|
||||
);
|
||||
}
|
||||
drawLineOut(x, y, color) {
|
||||
this.drawLine(
|
||||
x - 0.5 * this.config.unitSize,
|
||||
y + this.config.unitSize / 2,
|
||||
x + 0.5 * this.config.unitSize,
|
||||
y - this.config.unitSize / 2,
|
||||
color
|
||||
);
|
||||
}
|
||||
drawSymbol(symbol, columnNumber, rowNumber, color) {
|
||||
const y = this.height - this.config.unitSize * (rowNumber + 0.5);
|
||||
const x = this.config.unitSize * 0.5 * (columnNumber + 1);
|
||||
switch (symbol) {
|
||||
case '-':
|
||||
if (columnNumber % 2 === 1) {
|
||||
this.drawLineRight(x, y, color);
|
||||
}
|
||||
break;
|
||||
case '_':
|
||||
this.drawLineRight(x, y, color);
|
||||
break;
|
||||
case '*':
|
||||
this.drawNode(x, y, color);
|
||||
break;
|
||||
case '|':
|
||||
this.drawLineUp(x, y, color);
|
||||
break;
|
||||
case '/':
|
||||
this.drawLineOut(x, y, color);
|
||||
break;
|
||||
case '\\':
|
||||
this.drawLineIn(x, y, color);
|
||||
break;
|
||||
case '.':
|
||||
case ' ':
|
||||
break;
|
||||
default:
|
||||
console.error('Unknown symbol', symbol, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class GitGraph {
|
||||
constructor(canvas, rawRows, config) {
|
||||
this.rows = [];
|
||||
let maxWidth = 0;
|
||||
|
||||
for (let i = 0; i < rawRows.length; i++) {
|
||||
const rowStr = rawRows[i];
|
||||
maxWidth = Math.max(rowStr.replace(/([_\s.-])/g, '').length, maxWidth);
|
||||
|
||||
const rowArray = rowStr.split('');
|
||||
|
||||
this.rows.unshift(rowArray);
|
||||
}
|
||||
|
||||
this.currentFlows = [];
|
||||
this.previousFlows = [];
|
||||
|
||||
this.gitGraphCanvas = new GitGraphCanvas(
|
||||
canvas,
|
||||
maxWidth,
|
||||
this.rows.length,
|
||||
config
|
||||
);
|
||||
}
|
||||
|
||||
generateNewFlow(column) {
|
||||
let newId;
|
||||
|
||||
do {
|
||||
newId = generateRandomColorString();
|
||||
} while (this.hasFlow(newId, column));
|
||||
|
||||
return {id: newId, color: `#${newId}`};
|
||||
}
|
||||
|
||||
hasFlow(id, column) {
|
||||
// We want to find the flow with the current ID
|
||||
// Possible flows are those in the currentFlows
|
||||
// Or flows in previousFlows[column-2:...]
|
||||
for (
|
||||
let idx = column - 2 < 0 ? 0 : column - 2;
|
||||
idx < this.previousFlows.length;
|
||||
idx++
|
||||
) {
|
||||
if (this.previousFlows[idx] && this.previousFlows[idx].id === id) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
for (let idx = 0; idx < this.currentFlows.length; idx++) {
|
||||
if (this.currentFlows[idx] && this.currentFlows[idx].id === id) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
takePreviousFlow(column) {
|
||||
if (column < this.previousFlows.length && this.previousFlows[column]) {
|
||||
const flow = this.previousFlows[column];
|
||||
this.previousFlows[column] = null;
|
||||
return flow;
|
||||
}
|
||||
return this.generateNewFlow(column);
|
||||
}
|
||||
|
||||
draw() {
|
||||
if (this.rows.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.currentFlows = new Array(this.rows[0].length);
|
||||
|
||||
// Generate flows for the first row - I do not believe that this can contain '_', '-', '.'
|
||||
for (let column = 0; column < this.rows[0].length; column++) {
|
||||
if (this.rows[0][column] === ' ') {
|
||||
continue;
|
||||
}
|
||||
this.currentFlows[column] = this.generateNewFlow(column);
|
||||
}
|
||||
|
||||
// Draw the first row
|
||||
for (let column = 0; column < this.rows[0].length; column++) {
|
||||
const symbol = this.rows[0][column];
|
||||
const color = this.currentFlows[column] ? this.currentFlows[column].color : '';
|
||||
this.gitGraphCanvas.drawSymbol(symbol, column, 0, color);
|
||||
}
|
||||
|
||||
for (let row = 1; row < this.rows.length; row++) {
|
||||
// Done previous row - step up the row
|
||||
const currentRow = this.rows[row];
|
||||
const previousRow = this.rows[row - 1];
|
||||
|
||||
this.previousFlows = this.currentFlows;
|
||||
this.currentFlows = new Array(currentRow.length);
|
||||
|
||||
// Set flows for this row
|
||||
for (let column = 0; column < currentRow.length; column++) {
|
||||
column = this.setFlowFor(column, currentRow, previousRow);
|
||||
}
|
||||
|
||||
// Draw this row
|
||||
for (let column = 0; column < currentRow.length; column++) {
|
||||
const symbol = currentRow[column];
|
||||
const color = this.currentFlows[column] ? this.currentFlows[column].color : '';
|
||||
this.gitGraphCanvas.drawSymbol(symbol, column, row, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setFlowFor(column, currentRow, previousRow) {
|
||||
const symbol = currentRow[column];
|
||||
switch (symbol) {
|
||||
case '|':
|
||||
case '*':
|
||||
return this.setUpFlow(column, currentRow, previousRow);
|
||||
case '/':
|
||||
return this.setOutFlow(column, currentRow, previousRow);
|
||||
case '\\':
|
||||
return this.setInFlow(column, currentRow, previousRow);
|
||||
case '_':
|
||||
return this.setRightFlow(column, currentRow, previousRow);
|
||||
case '-':
|
||||
return this.setLeftFlow(column, currentRow, previousRow);
|
||||
case ' ':
|
||||
// In space no one can hear you flow ... (?)
|
||||
return column;
|
||||
default:
|
||||
// Unexpected so let's generate a new flow and wait for bug-reports
|
||||
this.currentFlows[column] = this.generateNewFlow(column);
|
||||
return column;
|
||||
}
|
||||
}
|
||||
|
||||
// setUpFlow handles '|' or '*' - returns the last column that was set
|
||||
// generally we prefer to take the left most flow from the previous row
|
||||
setUpFlow(column, currentRow, previousRow) {
|
||||
// If ' |/' or ' |_'
|
||||
// '/|' '/|' -> Take the '|' flow directly beneath us
|
||||
if (
|
||||
column + 1 < currentRow.length &&
|
||||
(currentRow[column + 1] === '/' || currentRow[column + 1] === '_') &&
|
||||
column < previousRow.length &&
|
||||
(previousRow[column] === '|' || previousRow[column] === '*') &&
|
||||
previousRow[column - 1] === '/'
|
||||
) {
|
||||
this.currentFlows[column] = this.takePreviousFlow(column);
|
||||
return column;
|
||||
}
|
||||
|
||||
// If ' |/' or ' |_'
|
||||
// '/ ' '/ ' -> Take the '/' flow from the preceding column
|
||||
if (
|
||||
column + 1 < currentRow.length &&
|
||||
(currentRow[column + 1] === '/' || currentRow[column + 1] === '_') &&
|
||||
column - 1 < previousRow.length &&
|
||||
previousRow[column - 1] === '/'
|
||||
) {
|
||||
this.currentFlows[column] = this.takePreviousFlow(column - 1);
|
||||
return column;
|
||||
}
|
||||
|
||||
// If ' |'
|
||||
// '/' -> Take the '/' flow - (we always prefer the left-most flow)
|
||||
if (
|
||||
column > 0 &&
|
||||
column - 1 < previousRow.length &&
|
||||
previousRow[column - 1] === '/'
|
||||
) {
|
||||
this.currentFlows[column] = this.takePreviousFlow(column - 1);
|
||||
return column;
|
||||
}
|
||||
|
||||
// If '|' OR '|' take the '|' flow
|
||||
// '|' '*'
|
||||
if (
|
||||
column < previousRow.length &&
|
||||
(previousRow[column] === '|' || previousRow[column] === '*')
|
||||
) {
|
||||
this.currentFlows[column] = this.takePreviousFlow(column);
|
||||
return column;
|
||||
}
|
||||
|
||||
// If '| ' keep the '\' flow
|
||||
// ' \'
|
||||
if (column + 1 < previousRow.length && previousRow[column + 1] === '\\') {
|
||||
this.currentFlows[column] = this.takePreviousFlow(column + 1);
|
||||
return column;
|
||||
}
|
||||
|
||||
// Otherwise just create a new flow - probably this is an error...
|
||||
this.currentFlows[column] = this.generateNewFlow(column);
|
||||
return column;
|
||||
}
|
||||
|
||||
// setOutFlow handles '/' - returns the last column that was set
|
||||
// generally we prefer to take the left most flow from the previous row
|
||||
setOutFlow(column, currentRow, previousRow) {
|
||||
// If '_/' -> keep the '_' flow
|
||||
if (column > 0 && currentRow[column - 1] === '_') {
|
||||
this.currentFlows[column] = this.currentFlows[column - 1];
|
||||
return column;
|
||||
}
|
||||
|
||||
// If '_|/' -> keep the '_' flow
|
||||
if (
|
||||
column > 1 &&
|
||||
(currentRow[column - 1] === '|' || currentRow[column - 1] === '*') &&
|
||||
currentRow[column - 2] === '_'
|
||||
) {
|
||||
this.currentFlows[column] = this.currentFlows[column - 2];
|
||||
return column;
|
||||
}
|
||||
|
||||
// If '|/'
|
||||
// '/' -> take the '/' flow (if it is still available)
|
||||
if (
|
||||
column > 1 &&
|
||||
currentRow[column - 1] === '|' &&
|
||||
column - 2 < previousRow.length &&
|
||||
previousRow[column - 2] === '/'
|
||||
) {
|
||||
this.currentFlows[column] = this.takePreviousFlow(column - 2);
|
||||
return column;
|
||||
}
|
||||
|
||||
// If ' /'
|
||||
// '/' -> take the '/' flow, but transform the symbol to '|' due to our spacing
|
||||
// This should only happen if there are 3 '/' - in a row so we don't need to be cleverer here
|
||||
if (
|
||||
column > 0 &&
|
||||
currentRow[column - 1] === ' ' &&
|
||||
column - 1 < previousRow.length &&
|
||||
previousRow[column - 1] === '/'
|
||||
) {
|
||||
this.currentFlows[column] = this.takePreviousFlow(column - 1);
|
||||
currentRow[column] = '|';
|
||||
return column;
|
||||
}
|
||||
|
||||
// If ' /'
|
||||
// '|' -> take the '|' flow
|
||||
if (
|
||||
column > 0 &&
|
||||
currentRow[column - 1] === ' ' &&
|
||||
column - 1 < previousRow.length &&
|
||||
(previousRow[column - 1] === '|' || previousRow[column - 1] === '*')
|
||||
) {
|
||||
this.currentFlows[column] = this.takePreviousFlow(column - 1);
|
||||
return column;
|
||||
}
|
||||
|
||||
// If '/' <- Not sure this ever happens... but take the '\' flow
|
||||
// '\'
|
||||
if (column < previousRow.length && previousRow[column] === '\\') {
|
||||
this.currentFlows[column] = this.takePreviousFlow(column);
|
||||
return column;
|
||||
}
|
||||
|
||||
// Otherwise just generate a new flow and wait for bug-reports...
|
||||
this.currentFlows[column] = this.generateNewFlow(column);
|
||||
return column;
|
||||
}
|
||||
|
||||
// setInFlow handles '\' - returns the last column that was set
|
||||
// generally we prefer to take the left most flow from the previous row
|
||||
setInFlow(column, currentRow, previousRow) {
|
||||
// If '\?'
|
||||
// '/?' -> take the '/' flow
|
||||
if (column < previousRow.length && previousRow[column] === '/') {
|
||||
this.currentFlows[column] = this.takePreviousFlow(column);
|
||||
return column;
|
||||
}
|
||||
|
||||
// If '\?'
|
||||
// ' \' -> take the '\' flow and reassign to '|'
|
||||
// This should only happen if there are 3 '\' - in a row so we don't need to be cleverer here
|
||||
if (column + 1 < previousRow.length && previousRow[column + 1] === '\\') {
|
||||
this.currentFlows[column] = this.takePreviousFlow(column + 1);
|
||||
currentRow[column] = '|';
|
||||
return column;
|
||||
}
|
||||
|
||||
// If '\?'
|
||||
// ' |' -> take the '|' flow
|
||||
if (
|
||||
column + 1 < previousRow.length &&
|
||||
(previousRow[column + 1] === '|' || previousRow[column + 1] === '*')
|
||||
) {
|
||||
this.currentFlows[column] = this.takePreviousFlow(column + 1);
|
||||
return column;
|
||||
}
|
||||
|
||||
// Otherwise just generate a new flow and wait for bug-reports if we're wrong...
|
||||
this.currentFlows[column] = this.generateNewFlow(column);
|
||||
return column;
|
||||
}
|
||||
|
||||
// setRightFlow handles '_' - returns the last column that was set
|
||||
// generally we prefer to take the left most flow from the previous row
|
||||
setRightFlow(column, currentRow, previousRow) {
|
||||
// if '__' keep the '_' flow
|
||||
if (column > 0 && currentRow[column - 1] === '_') {
|
||||
this.currentFlows[column] = this.currentFlows[column - 1];
|
||||
return column;
|
||||
}
|
||||
|
||||
// if '_|_' -> keep the '_' flow
|
||||
if (
|
||||
column > 1 &&
|
||||
currentRow[column - 1] === '|' &&
|
||||
currentRow[column - 2] === '_'
|
||||
) {
|
||||
this.currentFlows[column] = this.currentFlows[column - 2];
|
||||
return column;
|
||||
}
|
||||
|
||||
// if ' _' -> take the '/' flow
|
||||
// '/ '
|
||||
if (
|
||||
column > 0 &&
|
||||
column - 1 < previousRow.length &&
|
||||
previousRow[column - 1] === '/'
|
||||
) {
|
||||
this.currentFlows[column] = this.takePreviousFlow(column - 1);
|
||||
return column;
|
||||
}
|
||||
|
||||
// if ' |_'
|
||||
// '/? ' -> take the '/' flow (this may cause generation...)
|
||||
// we can do this because we know that git graph
|
||||
// doesn't create compact graphs like: ' |_'
|
||||
// '//'
|
||||
if (
|
||||
column > 1 &&
|
||||
column - 2 < previousRow.length &&
|
||||
previousRow[column - 2] === '/'
|
||||
) {
|
||||
this.currentFlows[column] = this.takePreviousFlow(column - 2);
|
||||
return column;
|
||||
}
|
||||
|
||||
// There really shouldn't be another way of doing this - generate and wait for bug-reports...
|
||||
|
||||
this.currentFlows[column] = this.generateNewFlow(column);
|
||||
return column;
|
||||
}
|
||||
|
||||
// setLeftFlow handles '----.' - returns the last column that was set
|
||||
// generally we prefer to take the left most flow from the previous row that terminates this left recursion
|
||||
setLeftFlow(column, currentRow, previousRow) {
|
||||
// This is: '----------.' or the like
|
||||
// ' \ \ /|\'
|
||||
|
||||
// Find the end of the '-' or nearest '/|\' in the previousRow :
|
||||
let originalColumn = column;
|
||||
let flow;
|
||||
for (; column < currentRow.length && currentRow[column] === '-'; column++) {
|
||||
if (column > 0 && column - 1 < previousRow.length && previousRow[column - 1] === '/') {
|
||||
flow = this.takePreviousFlow(column - 1);
|
||||
break;
|
||||
} else if (column < previousRow.length && previousRow[column] === '|') {
|
||||
flow = this.takePreviousFlow(column);
|
||||
break;
|
||||
} else if (
|
||||
column + 1 < previousRow.length &&
|
||||
previousRow[column + 1] === '\\'
|
||||
) {
|
||||
flow = this.takePreviousFlow(column + 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// if we have a flow then we found a '/|\' in the previousRow
|
||||
if (flow) {
|
||||
for (; originalColumn < column + 1; originalColumn++) {
|
||||
this.currentFlows[originalColumn] = flow;
|
||||
}
|
||||
return column;
|
||||
}
|
||||
|
||||
// If the symbol in the column is not a '.' then there's likely an error
|
||||
if (currentRow[column] !== '.') {
|
||||
// It really should end in a '.' but this one doesn't...
|
||||
// 1. Step back - we don't want to eat this column
|
||||
column--;
|
||||
// 2. Generate a new flow and await bug-reports...
|
||||
this.currentFlows[column] = this.generateNewFlow(column);
|
||||
|
||||
// 3. Assign all of the '-' to the same flow.
|
||||
for (; originalColumn < column; originalColumn++) {
|
||||
this.currentFlows[originalColumn] = this.currentFlows[column];
|
||||
}
|
||||
return column;
|
||||
}
|
||||
|
||||
// We have a terminal '.' eg. the current row looks like '----.'
|
||||
// the previous row should look like one of '/|\' eg. ' \'
|
||||
if (column > 0 && column - 1 < previousRow.length && previousRow[column - 1] === '/') {
|
||||
flow = this.takePreviousFlow(column - 1);
|
||||
} else if (column < previousRow.length && previousRow[column] === '|') {
|
||||
flow = this.takePreviousFlow(column);
|
||||
} else if (
|
||||
column + 1 < previousRow.length &&
|
||||
previousRow[column + 1] === '\\'
|
||||
) {
|
||||
flow = this.takePreviousFlow(column + 1);
|
||||
} else {
|
||||
// Again unexpected so let's generate and wait the bug-report
|
||||
flow = this.generateNewFlow(column);
|
||||
}
|
||||
|
||||
// Assign all of the rest of the ----. to this flow.
|
||||
for (; originalColumn < column + 1; originalColumn++) {
|
||||
this.currentFlows[originalColumn] = flow;
|
||||
}
|
||||
|
||||
return column;
|
||||
}
|
||||
}
|
||||
|
||||
function generateRandomColorString() {
|
||||
const chars = '0123456789ABCDEF';
|
||||
const stringLength = 6;
|
||||
let randomString = '',
|
||||
rnum,
|
||||
i;
|
||||
for (i = 0; i < stringLength; i++) {
|
||||
rnum = Math.floor(Math.random() * chars.length);
|
||||
randomString += chars.substring(rnum, rnum + 1);
|
||||
}
|
||||
|
||||
return randomString;
|
||||
}
|
||||
|
||||
export default async function initGitGraph() {
|
||||
const graphCanvas = document.getElementById('graph-canvas');
|
||||
if (!graphCanvas || !graphCanvas.getContext) return;
|
||||
const graphContainer = document.getElementById('git-graph-container');
|
||||
if (!graphContainer) return;
|
||||
|
||||
// Grab the raw graphList
|
||||
const graphList = [];
|
||||
$('#graph-raw-list li span.node-relation').each(function () {
|
||||
graphList.push($(this).text());
|
||||
$('#flow-color-monochrome').on('click', () => {
|
||||
$('#flow-color-monochrome').addClass('active');
|
||||
$('#flow-color-colored').removeClass('active');
|
||||
$('#git-graph-container').removeClass('colored').addClass('monochrome');
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
params.set('mode', 'monochrome');
|
||||
const queryString = params.toString();
|
||||
if (queryString) {
|
||||
window.history.replaceState({}, '', `?${queryString}`);
|
||||
} else {
|
||||
window.history.replaceState({}, '', window.location.pathname);
|
||||
}
|
||||
$('.pagination a').each((_, that) => {
|
||||
const href = $(that).attr('href');
|
||||
if (!href) return;
|
||||
const url = new URL(href, window.location);
|
||||
const params = url.searchParams;
|
||||
params.set('mode', 'monochrome');
|
||||
url.search = `?${params.toString()}`;
|
||||
$(that).attr('href', url.href);
|
||||
});
|
||||
});
|
||||
$('#flow-color-colored').on('click', () => {
|
||||
$('#flow-color-colored').addClass('active');
|
||||
$('#flow-color-monochrome').removeClass('active');
|
||||
$('#git-graph-container').addClass('colored').removeClass('monochrome');
|
||||
$('.pagination a').each((_, that) => {
|
||||
const href = $(that).attr('href');
|
||||
if (!href) return;
|
||||
const url = new URL(href, window.location);
|
||||
const params = url.searchParams;
|
||||
params.delete('mode');
|
||||
url.search = `?${params.toString()}`;
|
||||
$(that).attr('href', url.href);
|
||||
});
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
params.delete('mode');
|
||||
const queryString = params.toString();
|
||||
if (queryString) {
|
||||
window.history.replaceState({}, '', `?${queryString}`);
|
||||
} else {
|
||||
window.history.replaceState({}, '', window.location.pathname);
|
||||
}
|
||||
});
|
||||
$('#git-graph-container').on('mouseenter', '#rev-list li', (e) => {
|
||||
const flow = $(e.currentTarget).data('flow');
|
||||
if (flow === 0) return;
|
||||
$(`#flow-${flow}`).addClass('highlight');
|
||||
$(e.currentTarget).addClass('hover');
|
||||
$(`#rev-list li[data-flow='${flow}']`).addClass('highlight');
|
||||
});
|
||||
$('#git-graph-container').on('mouseleave', '#rev-list li', (e) => {
|
||||
const flow = $(e.currentTarget).data('flow');
|
||||
if (flow === 0) return;
|
||||
$(`#flow-${flow}`).removeClass('highlight');
|
||||
$(e.currentTarget).removeClass('hover');
|
||||
$(`#rev-list li[data-flow='${flow}']`).removeClass('highlight');
|
||||
});
|
||||
$('#git-graph-container').on('mouseenter', '#rel-container .flow-group', (e) => {
|
||||
$(e.currentTarget).addClass('highlight');
|
||||
const flow = $(e.currentTarget).data('flow');
|
||||
$(`#rev-list li[data-flow='${flow}']`).addClass('highlight');
|
||||
});
|
||||
$('#git-graph-container').on('mouseleave', '#rel-container .flow-group', (e) => {
|
||||
$(e.currentTarget).removeClass('highlight');
|
||||
const flow = $(e.currentTarget).data('flow');
|
||||
$(`#rev-list li[data-flow='${flow}']`).removeClass('highlight');
|
||||
});
|
||||
$('#git-graph-container').on('mouseenter', '#rel-container .flow-commit', (e) => {
|
||||
const rev = $(e.currentTarget).data('rev');
|
||||
$(`#rev-list li#commit-${rev}`).addClass('hover');
|
||||
});
|
||||
$('#git-graph-container').on('mouseleave', '#rel-container .flow-commit', (e) => {
|
||||
const rev = $(e.currentTarget).data('rev');
|
||||
$(`#rev-list li#commit-${rev}`).removeClass('hover');
|
||||
});
|
||||
|
||||
// Define some drawing parameters
|
||||
const config = {
|
||||
unitSize: 20,
|
||||
lineWidth: 3,
|
||||
nodeRadius: 4
|
||||
};
|
||||
|
||||
|
||||
const gitGraph = new GitGraph(graphCanvas, graphList, config);
|
||||
gitGraph.draw();
|
||||
graphCanvas.closest('#git-graph-container').classList.add('in');
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue