@git-temporal/git-temporal-react
Version:
<!-- START doctoc generated TOC please keep comment here to allow auto update --> <!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
251 lines (250 loc) • 9.88 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
result["default"] = mod;
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
const react_1 = __importDefault(require("react"));
const lodash_1 = require("lodash");
const logger_1 = require("app/utilities/logger");
const styles_1 = require("app/styles");
const d3 = __importStar(require("d3"));
require('d3-selection-multi');
const d3_1 = require("app/utilities/d3");
const dates_1 = require("app/utilities/dates");
const commits_1 = require("app/utilities/commits");
d3_1.addMoveToFront(d3);
const outerStyle = {
_extends: 'fill',
position: 'relative',
overflow: 'hidden',
};
const blobStyle = {
fill: '@colors.blobColor',
opacity: 0.2,
};
const highlightedBlobStyle = {
fill: '@colors.selected',
opacity: 0.5,
};
const markerStyle = {
position: 'absolute',
height: 130,
width: 3,
opacity: 0.5,
zIndex: 1,
top: 0,
};
const startDateMarkerStyle = {
_extends: markerStyle,
backgroundColor: '@colors.leftRevColor',
};
const endDateMarkerStyle = {
_extends: markerStyle,
backgroundColor: '@colors.rightRevColor',
};
const LEFT_PADDING = 20;
const PADDING = 20;
class TimeplotGraph extends react_1.default.Component {
constructor(props) {
super(props);
this.getScrollWidth = () => {
const element = this.timeplotGraphRef.current;
return element && element.scrollWidth;
};
this.scrollLeft = newScrollLeft => {
const element = this.timeplotGraphRef.current;
element.scrollLeft = newScrollLeft;
};
this.onMouseEnter = evt => {
this.props.onMouseEnter && this.props.onMouseEnter(evt);
};
this.onMouseLeave = evt => {
this.props.onMouseLeave && this.props.onMouseLeave(evt);
};
this.onMouseMove = evt => {
if (!this.props.onMouseMove) {
return;
}
const dates = this.getDatesForMouseEvent(evt);
this.setState({ hoverMarkerLeft: dates.relativeLeft });
this.props.onMouseMove(evt, dates);
};
this.onMouseDown = evt => {
if (!this.props.onMouseDown) {
return;
}
const dates = this.getDatesForMouseEvent(evt);
this.props.onMouseDown(evt, dates);
};
this.onMouseUp = evt => {
if (!this.props.onMouseUp) {
return;
}
const dates = this.getDatesForMouseEvent(evt);
this.props.onMouseUp(evt, dates);
};
this.timeplotGraphRef = react_1.default.createRef();
this.renderTimeplotGraph = this.renderTimeplotGraph.bind(this);
this.updateTimeplotGraphThrottled = lodash_1.throttle(this.updateTimeplotGraph, 1000);
}
componentDidMount() {
this.renderTimeplotGraph();
window && window.addEventListener('resize', this.renderTimeplotGraph);
}
componentDidUpdate(prevProps) {
if (prevProps.forceRender !== this.props.forceRender ||
prevProps.earliestCommitDate !== this.props.earliestCommitDate ||
prevProps.latestCommitDate !== this.props.latestCommitDate ||
JSON.stringify(prevProps.commits) !== JSON.stringify(this.props.commits)) {
this.renderTimeplotGraph();
this.forceUpdate();
this.updateTimeplotGraphThrottled();
}
else if (prevProps.highlightedCommitIds !== this.props.highlightedCommitIds) {
this.updateHighlightedCommits();
}
}
componentWillUnmount() {
window && window.removeEventListener('resize', this.renderTimeplotGraph);
}
focus() {
this.timeplotGraphRef.current.focus();
}
render() {
const { startDate, endDate } = this.props;
const startDateStyle = startDate && this.xScale
? { left: this.xScale && this.xScale(dates_1.dateFromEpochDate(startDate)) }
: {};
const endDateStyle = endDate && this.xScale
? { left: this.xScale(dates_1.dateFromEpochDate(endDate)) }
: {};
return (react_1.default.createElement("div", { style: styles_1.style(outerStyle, this.props.style), onMouseEnter: this.onMouseEnter, onMouseLeave: this.onMouseLeave, onMouseMove: this.onMouseMove, onMouseDown: this.onMouseDown, onMouseUp: this.onMouseUp, "data-testid": "timeplotGraph" },
react_1.default.createElement("div", { style: styles_1.style('fill'), ref: this.timeplotGraphRef }),
startDate ? (react_1.default.createElement("div", { style: styles_1.style(startDateMarkerStyle, startDateStyle) })) : null,
endDate ? (react_1.default.createElement("div", { style: styles_1.style(endDateMarkerStyle, endDateStyle) })) : null));
}
getHeight() {
return ((this.props.height && this.props.height) ||
this.timeplotGraphRef.current.clientHeight);
}
updateHighlightedCommits() {
const explosionFactor = this.props.highlightedCommitIds.length < 5 ? 25 : 5;
this.svg
.selectAll('circle[data-selected="true"]')
.styles(styles_1.style(blobStyle))
.attr('data-selected', false);
this.svg
.selectAll(`circle`)
.filter(d => this.props.highlightedCommitIds.includes(d.id))
.moveToFront()
.styles(styles_1.style(highlightedBlobStyle))
.attr('data-selected', true)
.transition()
.duration(300)
.attr('r', d => this.rScale(d.linesAdded + d.linesDeleted || 0) * explosionFactor)
.transition()
.duration(800)
.attr('r', d => this.rScale(d.linesAdded + d.linesDeleted || 0));
}
clearTimeplotGraph() {
const element = this.timeplotGraphRef.current;
while (element.firstChild) {
element.removeChild(element.firstChild);
}
}
renderTimeplotGraph() {
const element = this.timeplotGraphRef.current;
this.clearTimeplotGraph();
this.svg = d3
.select(element)
.append('svg')
.attr('width', element.clientWidth)
.attr('height', this.getHeight());
this.calibrateScales();
this.renderAxis();
if (this.props.commits.length <= 0) {
element.innerHtml =
"<div class='placeholder'>No commits, nothing to see here.</div>";
return;
}
this.renderBlobs();
this.updateHighlightedCommits();
logger_1.debug(`TimeplotGraph: rendered`);
}
updateTimeplotGraph() {
this.calibrateScales();
this.renderBlobs();
this.updateHighlightedCommits();
}
calibrateScales() {
const element = this.timeplotGraphRef.current;
const { earliestCommitDate, latestCommitDate, commits } = this.props;
const w = element.clientWidth;
const h = this.getHeight();
const maxImpact = d3.max(commits.map(d => d.linesAdded + d.linesDeleted));
const minDate = dates_1.dateFromEpochDate(earliestCommitDate);
const maxDate = dates_1.dateFromEpochDate(latestCommitDate);
this.xScale = d3
.scaleTime()
.domain([minDate, maxDate])
.range([LEFT_PADDING, w - PADDING]);
this.yScale = d3
.scaleLinear()
.domain([0, 25])
.range([10, h - PADDING * 2 - 20]);
this.rScale = d3
.scalePow(10)
.domain([1, maxImpact > 10000 ? 10000 : maxImpact])
.range([3, 40])
.clamp(true);
}
renderAxis() {
const element = this.timeplotGraphRef.current;
const h = this.getHeight();
const textColor = styles_1.getStyleVar('colors', 'text');
const xAxis = d3
.axisBottom()
.scale(this.xScale)
.ticks(element.scrollWidth / 100);
const renderedXaxis = this.svg
.append('g')
.attr('class', 'axis')
.attr('transform', `translate(0, ${h - PADDING})`)
.call(xAxis);
renderedXaxis.selectAll('path,line').style('stroke', textColor);
renderedXaxis.selectAll('text').style('fill', textColor);
}
renderBlobs() {
return this.svg
.selectAll('circle')
.data(this.props.commits)
.enter()
.append('circle')
.attr('data-id', d => d.id)
.attr('cx', d => this.xScale(commits_1.getUTCDateOfCommit(d)))
.attr('cy', d => this.yScale(commits_1.getHourOfCommit(d) + 10))
.styles(styles_1.style(blobStyle))
.transition()
.duration(1000)
.attr('r', d => this.rScale(d.linesAdded + d.linesDeleted));
}
getDatesForMouseEvent(evt) {
const element = this.timeplotGraphRef.current;
const rect = element.getBoundingClientRect();
const relativeLeft = evt.clientX - rect.x + element.scrollLeft;
const exactDate = this.xScale.invert(relativeLeft);
const startLeft = relativeLeft < 0 ? 0 : relativeLeft;
const startDate = this.xScale.invert(startLeft);
const endLeft = relativeLeft + 10 > element.width ? element.width : relativeLeft + 10;
const endDate = this.xScale.invert(endLeft);
return { exactDate, startDate, endDate, relativeLeft };
}
}
exports.TimeplotGraph = TimeplotGraph;