@jupyterlab/git
Version:
A JupyterLab extension for version control using git
121 lines (120 loc) • 6.94 kB
JavaScript
import { closeIcon } from '@jupyterlab/ui-components';
import { Menu } from '@lumino/widgets';
import { addHistoryMenuItems } from '../commandsAndMenu';
import * as React from 'react';
import { hiddenButtonStyle } from '../style/ActionButtonStyle';
import { historySideBarStyle, noHistoryFoundStyle, selectedHistoryFileStyle, historySideBarWrapperStyle } from '../style/HistorySideBarStyle';
import { ContextCommandIDs, Git } from '../tokens';
import { openFileDiff } from '../utils';
import { ActionButton } from './ActionButton';
import { FileItem } from './FileItem';
import { PastCommitNode } from './PastCommitNode';
import { SinglePastCommitInfo } from './SinglePastCommitInfo';
import { GitCommitGraph } from './GitCommitGraph';
export const CONTEXT_COMMANDS = [ContextCommandIDs.gitTagAdd];
/**
* Returns a React component for displaying commit history.
*
* @param props - component properties
* @returns React element
*/
export const HistorySideBar = (props) => {
var _a;
/**
* Discards the selected file and shows the full history.
*/
const removeSelectedFile = () => {
props.model.selectedHistoryFile = null;
};
/**
* Commit info for 'Uncommitted Changes' history.
*/
const uncommitted = React.useMemo(() => {
var _a, _b, _c;
return {
author: props.trans.__('You'),
commit: `${((_a = props.model.selectedHistoryFile) === null || _a === void 0 ? void 0 : _a.status) === 'staged'
? Git.Diff.SpecialRef.INDEX
: Git.Diff.SpecialRef.WORKING}`,
pre_commits: ['HEAD'],
is_binary: (_c = (_b = props.commits[0]) === null || _b === void 0 ? void 0 : _b.is_binary) !== null && _c !== void 0 ? _c : false,
commit_msg: props.trans.__('Uncommitted Changes'),
date: props.trans.__('now')
};
}, [props.model.selectedHistoryFile]);
const commits = props.model.selectedHistoryFile &&
((_a = props.model.selectedHistoryFile) === null || _a === void 0 ? void 0 : _a.status) !== 'unmodified'
? [uncommitted, ...props.commits]
: props.commits;
const [expandedCommits, setExpandedCommits] = React.useState([]);
const [nodeHeights, setNodeHeights] = React.useState({});
const nodes = React.useRef({});
React.useEffect(() => {
const resizeObserver = new ResizeObserver(entries => {
for (const entry of entries) {
const borderBoxSize = Array.isArray(entry.borderBoxSize)
? entry.borderBoxSize[0]
: entry.borderBoxSize;
const offsetHeight = borderBoxSize.blockSize;
const sha = entry.target.id;
setNodeHeights(prev => ({ ...prev, [sha]: offsetHeight }));
}
});
props.commits.forEach(commit => resizeObserver.observe(nodes.current[commit.commit], {
box: 'border-box'
}));
return () => resizeObserver.disconnect();
}, [props.commits]);
/**
* Open the context menu on the advanced view
*
* @param selectedCommit The commit on which the context menu is opened
* @param event The click event
*/
const openContextMenu = (selectedCommit, event) => {
event.preventDefault();
const contextMenu = new Menu({ commands: props.commands });
const commands = [ContextCommandIDs.gitTagAdd];
addHistoryMenuItems(commands, contextMenu, selectedCommit);
contextMenu.open(event.clientX, event.clientY);
};
return (React.createElement("div", { className: historySideBarWrapperStyle },
!props.model.selectedHistoryFile && (React.createElement(GitCommitGraph, { commits: props.commits.map(commit => ({
sha: commit.commit,
parents: commit.pre_commits
})), getNodeHeight: (sha) => { var _a; return (_a = nodeHeights[sha]) !== null && _a !== void 0 ? _a : 55; } })),
React.createElement("ol", { className: historySideBarStyle },
!!props.model.selectedHistoryFile && (React.createElement(FileItem, { className: selectedHistoryFileStyle, model: props.model, trans: props.trans, actions: React.createElement(ActionButton, { className: hiddenButtonStyle, icon: closeIcon, title: props.trans.__('Discard file history'), onClick: removeSelectedFile }), file: props.model.selectedHistoryFile, onDoubleClick: removeSelectedFile })),
commits.length ? (commits.map((commit) => {
var _a, _b, _c;
const commonProps = {
commit,
branches: props.branches,
tagsList: props.tagsList,
model: props.model,
commands: props.commands,
trans: props.trans
};
// Only pass down callback when single file history is open
// and its diff is viewable
const onOpenDiff = props.model.selectedHistoryFile && !commit.is_binary
? openFileDiff(props.commands)(commit)((_a = commit.file_path) !== null && _a !== void 0 ? _a : props.model.selectedHistoryFile.to, !commit.is_binary, commit.previous_file_path)
: undefined;
const isReferenceCommit = commit.commit === ((_b = props.referenceCommit) === null || _b === void 0 ? void 0 : _b.commit);
const isChallengerCommit = commit.commit === ((_c = props.challengerCommit) === null || _c === void 0 ? void 0 : _c.commit);
return (React.createElement(PastCommitNode, { key: commit.commit, setRef: node => {
nodes.current[commit.commit] = node;
}, ...commonProps, isReferenceCommit: isReferenceCommit, isChallengerCommit: isChallengerCommit, onOpenDiff: onOpenDiff, onSelectForCompare: isChallengerCommit
? undefined
: props.onSelectForCompare(commit), onCompareWithSelected: isReferenceCommit || props.referenceCommit === null
? undefined
: props.onCompareWithSelected(commit), expanded: expandedCommits.includes(commit.commit), toggleCommitExpansion: sha => setExpandedCommits(prevExpandedCommits => {
if (prevExpandedCommits.includes(sha)) {
return prevExpandedCommits.filter(commit => commit !== sha);
}
else {
return [...prevExpandedCommits, sha];
}
}), contextMenu: openContextMenu }, !props.model.selectedHistoryFile && (React.createElement(SinglePastCommitInfo, { ...commonProps, onOpenDiff: openFileDiff(props.commands)(commit) }))));
})) : (React.createElement("li", { className: noHistoryFoundStyle }, props.trans.__('No history found.'))))));
};