UNPKG

@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
"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;