UNPKG

@jupyterlab/git

Version:

A JupyterLab extension for version control using git

121 lines (120 loc) 6.94 kB
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.')))))); };