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 -->

272 lines (271 loc) 12.1 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const react_1 = __importDefault(require("react")); const react_redux_1 = require("react-redux"); const lodash_1 = require("lodash"); const logger_1 = require("app/utilities/logger"); const styles_1 = require("app/styles"); const selectors_1 = require("app/selectors"); const setDates_1 = require("app/actions/setDates"); const commits_1 = require("app/actions/commits"); const debounce_1 = require("app/utilities/debounce"); const throttle_1 = require("app/utilities/throttle"); const ZoomContainer_1 = require("app/components/ZoomContainer"); const TimeplotGraph_1 = require("app/components/TimeplotGraph"); const EpochSpan_1 = require("app/components/EpochSpan"); const CommaNumber_1 = require("app/components/CommaNumber"); const SpinnerContainer_1 = require("app/components/SpinnerContainer"); const TimeplotPopup_1 = require("app/components/TimeplotPopup"); const commits_2 = require("app/utilities/commits"); const initialState = { hoverMarkerLeft: -40, scrollLeft: 0, popupOpen: false, timeplotRenders: 0, popupCommits: [], popupStartDate: null, popupEndDate: null, zoom: 100, customZooms: [], mouseInPopover: false, }; const GRAPH_HEIGHT = 120; const outerStyle = { overflow: 'visible', flex: `0 0 ${GRAPH_HEIGHT}px`, }; const graphContainerStyle = { _extends: ['altPanel', 'flexColumn'], flex: `0 0 ${GRAPH_HEIGHT}px`, minHeight: `${GRAPH_HEIGHT}px`, position: 'relative', marginTop: 5, overflow: 'hidden', }; const statsStyle = { _extends: 'normalText', width: '100%', textAlign: 'center', paddingTop: '@margins.small+px', paddingBottom: '@margins.small+px', position: 'absolute', top: '@margins.medium+px', textShadow: '2px 2px @colors.background', }; const statsTextStyle = { opacity: 0.8, backgroundColor: '@colors.background', }; const zoomStyle = { position: 'absolute', top: '@margins.small+px', zIndex: 1, }; const timeplotStyle = { _extends: 'fill', background: '@colors.background', color: '@colors.text', }; const markerStyle = { position: 'absolute', height: GRAPH_HEIGHT, width: 10, opacity: 0.5, zIndex: 1, top: 0, }; const hoverMarkerStyle = { _extends: markerStyle, backgroundColor: '@colors.selectable', }; class Timeplot extends react_1.default.Component { constructor(props) { super(props); this.state = initialState; this.onPopupClose = () => { this.setState({ popupOpen: false }); }; this.onScroll = scrollLeft => { this.setState({ scrollLeft }); }; this.onZoom = newZoom => { this.setState({ timeplotRenders: this.state.timeplotRenders + 1, zoom: newZoom, }); }; this.onMouseEnterPopup = evt => { this.setState({ mouseInPopover: true }); }; this.onMouseLeavePopup = evt => { this.setState({ mouseInPopover: false, popupOpen: false }); }; this.onMouseLeave = _evt => { lodash_1.delay(() => { if (!this.state.mouseInPopover) { this.setState({ popupOpen: false }); } }, 250); }; this.onMouseMove = (evt, { startDate, endDate, relativeLeft }) => { const popupCommits = commits_2.filterCommitsForSpan(this.props.commits, startDate, endDate); const { pageX, pageY } = evt; if (!this.lastMouseMoveCoords || pageY >= this.lastMouseMoveCoords.pageY) { this.setState({ popupCommits, popupOpen: true, popupStartDate: startDate, popupEndDate: endDate, hoverMarkerLeft: relativeLeft + 3, }); } this.lastMouseMoveCoords = { pageX, pageY }; }; this.onMouseDown = (evt, { startDate }) => { logger_1.debug('Timeplot: onMouseDown', evt.shiftKey, startDate); evt.preventDefault(); this.lastMouseDownDate = startDate; const endDate = this.getEndDateForEvent(evt); this.setDates(startDate, endDate); }; this.onMouseUp = (evt, { startDate }) => { logger_1.debug('Timeplot: onMouseUp', evt.shiftKey, startDate, this.lastMouseDownDate); evt.preventDefault(); if (!this.lastMouseDownDate) { return; } if (startDate.toUTCString() !== this.lastMouseDownDate.toUTCString()) { this.setDates(this.lastMouseDownDate, startDate); } this.lastMouseDownDate = null; }; this.onMouseHoverMarker = evt => { this.setState({ hoverMarkerLeft: this.state.hoverMarkerLeft + (evt.pageX - evt.clientX) + 3, }); }; this.onCommitSelected = (evt, commit, single) => { evt.stopPropagation(); const { dispatch, commits } = this.props; if (single) { dispatch(commits_1.selectSingleCommit(commit)); } else { const epochAuthorDate = commit.authorDate * 1000; const endDate = this.getEndDateForEvent(evt); this.setDates(epochAuthorDate, endDate); } }; this.lastRerenderRequestedAt = Date.now(); this.timeplotRef = react_1.default.createRef(); this.outerRef = react_1.default.createRef(); this.debouncedOnMouseLeave = debounce_1.debounce(this.onMouseLeave, 100); this.debouncedOnMouseMove = throttle_1.throttle(this.onMouseMove, 100); } componentDidUpdate(prevProps, prevState) { if (!this.timeplotRef.current) { return; } this.timeplotRef.current.focus(); if (prevProps.startDate !== this.props.startDate || prevProps.endDate !== this.props.endDate) { this.addCustomZooms(); } if (this.lastRerenderRequestedAt < this.props.rerenderRequestedAt) { logger_1.debug('forcing timeplot rerender'); this.lastRerenderRequestedAt = this.props.rerenderRequestedAt; this.setState({ timeplotRenders: this.state.timeplotRenders + 1 }); } if (prevState.zoom !== this.state.zoom) { this.scrollToStartDate(); } } render() { const { commits = [], isFetching, startDate, endDate, earliestCommitDate, latestCommitDate, highlightedCommitIds, totalCommits, } = this.props; const outerLeft = (this.outerRef.current && this.outerRef.current.getBoundingClientRect().x) || 0; const popupLeft = this.state.hoverMarkerLeft - this.state.scrollLeft < TimeplotPopup_1.TIMEPLOT_POPUP_WIDTH + 20 ? this.state.hoverMarkerLeft - this.state.scrollLeft + outerLeft - 20 : this.state.hoverMarkerLeft - this.state.scrollLeft - TimeplotPopup_1.TIMEPLOT_POPUP_WIDTH + outerLeft + 20; return (react_1.default.createElement("div", { style: styles_1.style(outerStyle), ref: this.outerRef }, react_1.default.createElement("div", { style: styles_1.style(graphContainerStyle) }, react_1.default.createElement(SpinnerContainer_1.SpinnerContainer, { isSpinning: !commits || commits.length === 0, spinnerImageSize: 0 }, react_1.default.createElement(ZoomContainer_1.ZoomContainer, { onZoom: this.onZoom, onMouseLeave: this.debouncedOnMouseLeave, onScroll: this.onScroll, customZooms: this.state.customZooms, scrollLeft: this.state.scrollLeft, style: styles_1.style(zoomStyle) }, react_1.default.createElement(TimeplotGraph_1.TimeplotGraph, { forceRender: this.state.timeplotRenders, commits: this.props.commits, style: styles_1.style(timeplotStyle), ref: this.timeplotRef, height: GRAPH_HEIGHT, highlightedCommitIds: highlightedCommitIds, startDate: startDate, endDate: endDate, earliestCommitDate: earliestCommitDate, latestCommitDate: latestCommitDate, onMouseMove: this.debouncedOnMouseMove, onMouseDown: this.onMouseDown, onMouseUp: this.onMouseUp }), react_1.default.createElement("div", { style: styles_1.style(hoverMarkerStyle, { left: this.state.hoverMarkerLeft, }), onMouseMove: this.onMouseHoverMarker, onMouseEnter: this.onMouseHoverMarker })), react_1.default.createElement("div", { style: styles_1.style(statsStyle) }, react_1.default.createElement("span", { style: styles_1.style(statsTextStyle) }, !isFetching ? 'Total of ' : 'Retrieving ', react_1.default.createElement(CommaNumber_1.CommaNumber, { value: commits.length }), isFetching && (react_1.default.createElement("span", null, ' ', "of ", react_1.default.createElement(CommaNumber_1.CommaNumber, { value: totalCommits }))), react_1.default.createElement("span", null, " commits by "), react_1.default.createElement(CommaNumber_1.CommaNumber, { value: this.props.authors }), react_1.default.createElement("span", null, " authors spanning "), react_1.default.createElement(EpochSpan_1.EpochSpan, { firstEpochTime: earliestCommitDate, secondEpochTime: latestCommitDate }))))), react_1.default.createElement(TimeplotPopup_1.TimeplotPopup, { commits: this.state.popupCommits, isOpen: this.state.popupOpen, startDate: this.state.popupStartDate, endDate: this.state.popupEndDate, left: popupLeft, onClose: this.onPopupClose, onCommitSelected: this.onCommitSelected, onMouseEnter: this.onMouseEnterPopup, onMouseLeave: this.onMouseLeavePopup }))); } setDates(startDate, endDate) { const { dispatch } = this.props; dispatch(setDates_1.setDates(startDate, endDate)); } scrollToStartDate() { const { startDate } = this.props; if (this.state.zoom <= 100) { this.setState({ scrollLeft: 0 }); } else if (startDate && this.timeplotRef.current) { const { xScale } = this.timeplotRef.current; this.setState({ scrollLeft: xScale(startDate * 1000) - 30 }); } } addCustomZooms() { const { startDate, endDate } = this.props; if (!startDate || !this.timeplotRef.current) { this.setState({ customZooms: [] }); return; } const { xScale, getScrollWidth } = this.timeplotRef.current; const defaultedEndDate = endDate ? endDate * 1000 : new Date(); const spanLeft = xScale(startDate * 1000); const spanRight = xScale(defaultedEndDate); const span = spanRight - spanLeft + 15; if (span <= 0) { this.setState({ customZooms: [] }); return; } const scrollWidth = getScrollWidth(); this.setState({ customZooms: [ { value: Math.floor((scrollWidth / span) * 100), label: 'Zoom to selected time span', }, ], }); } getEndDateForEvent(evt) { return evt.shiftKey && this.props.endDate ? this.props.endDate * 1000 : evt.shiftKey && this.props.startDate ? this.props.startDate * 1000 : null; } } exports.Timeplot = Timeplot; exports.default = react_redux_1.connect(selectors_1.getTimeplotContainerState)(Timeplot);