UNPKG

@jupyterlab/git

Version:

A JupyterLab extension for version control using git

190 lines (189 loc) 10 kB
import { Notification } from '@jupyterlab/apputils'; import ClearIcon from '@mui/icons-material/Clear'; import Dialog from '@mui/material/Dialog'; import DialogActions from '@mui/material/DialogActions'; import * as React from 'react'; import { classes } from 'typestyle'; import { actionsWrapperClass, branchDialogClass, buttonClass, cancelButtonClass, closeButtonClass, contentWrapperClass, createButtonClass, errorMessageClass, filterClass, filterClearClass, filterInputClass, filterWrapperClass, nameInputClass, titleClass, titleWrapperClass } from '../style/NewBranchDialog'; import { activeListItemClass, commitBodyClass, commitHeaderBoldClass, commitHeaderClass, commitHeaderItemClass, commitItemBoldClass, commitWrapperClass, historyDialogBoxStyle, historyDialogBoxWrapperStyle } from '../style/NewTagDialog'; import { Git } from '../tokens'; import { GitCommitGraph } from './GitCommitGraph'; export const DialogBoxCommitGraph = (props) => { const { filter, baseCommitId, updateBaseCommitId } = props; const pastCommits = props.pastCommits .filter(commit => !filter || commit.commit_msg.toLowerCase().includes(filter.toLowerCase())) .slice(); 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 })); } }); pastCommits.forEach(commit => { const node = nodes.current[commit.commit]; resizeObserver.observe(node, { box: 'border-box' }); }); return () => resizeObserver.disconnect(); }, [pastCommits]); let isFilter; if (filter === '') { isFilter = true; } if (props.isSingleCommit === true) { isFilter = false; } return (React.createElement("div", { className: historyDialogBoxWrapperStyle }, isFilter && (React.createElement(GitCommitGraph, { commits: pastCommits.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: historyDialogBoxStyle }, pastCommits.map((commit, index) => { return (React.createElement(SingleCommitNode, { key: index, commit: commit, baseCommitId: baseCommitId, trans: props.trans, setRef: node => { nodes.current[commit.commit] = node; }, updateBaseCommitId: updateBaseCommitId })); })))); }; export const SingleCommitNode = (props) => { const { baseCommitId, updateBaseCommitId } = props; const isLatest = props.commit.commit === baseCommitId; let isBold; if (isLatest) { isBold = true; } /** * Returns a callback which is invoked upon clicking a commit name. * * @param commit - commit * @returns callback */ const onCommitClickFactory = React.useCallback((commitId) => { return onClick; /** * Callback invoked upon clicking a commit. * * @private * @param event - event object */ function onClick() { updateBaseCommitId(commitId); } }, [baseCommitId]); return (React.createElement("li", { id: props.commit.commit, ref: props.setRef, className: classes(commitWrapperClass, isLatest ? activeListItemClass : null), title: props.trans.__('Create a new tag pointing to commit %1: %2 by %3', props.commit.commit in Git.Diff.SpecialRef ? Git.Diff.SpecialRef[+props.commit.commit] : props.commit.commit.slice(0, 7), props.commit.commit_msg, props.commit.author), onClick: onCommitClickFactory(props.commit.commit) }, React.createElement("div", { className: commitHeaderClass }, React.createElement("span", { className: classes(commitHeaderItemClass, isBold ? commitItemBoldClass : null) }, props.commit.author), React.createElement("span", { className: classes(commitHeaderItemClass, isBold ? commitItemBoldClass : null) }, +props.commit.commit in Git.Diff.SpecialRef ? Git.Diff.SpecialRef[+props.commit.commit] : props.commit.commit.slice(0, 7)), React.createElement("span", { className: classes(commitHeaderItemClass, isBold ? commitItemBoldClass : null) }, props.commit.date)), React.createElement("div", { className: classes(commitBodyClass, isBold ? commitHeaderBoldClass : null) }, props.commit.commit_msg))); }; export const NewTagDialogBox = (props) => { var _a, _b; const [nameState, setNameState] = React.useState(''); const [baseCommitIdState, setBaseCommitIdState] = React.useState((_b = (_a = props.pastCommits[0]) === null || _a === void 0 ? void 0 : _a.commit) !== null && _b !== void 0 ? _b : null); const [filterState, setFilterState] = React.useState(''); const [errorState, setErrorState] = React.useState(''); /** * Callback invoked upon closing the dialog. * * @param event - event object */ const onClose = () => { props.onClose(); setNameState(''); setFilterState(''); setErrorState(''); }; /** * Callback invoked upon a change to the menu filter. * * @param event - event object */ const onFilterChange = React.useCallback((event) => { setFilterState(event.target.value); }, [filterState]); /** * Callback invoked to reset the menu filter. */ const resetFilter = () => { setFilterState(''); }; /** * Callback invoked upon a change to the tag name input element. * * @param event - event object */ const onNameChange = React.useCallback((event) => { setNameState(event.target.value); setErrorState(''); }, [nameState]); /** * Creates a new tag. * * @param tag - tag name * @returns promise which resolves upon attempting to create a new tag */ const createTag = async () => { const tagName = nameState; const baseCommitId = baseCommitIdState; const id = Notification.emit(props.trans.__('Creating tag…'), 'in-progress', { autoClose: false }); try { await props.model.setTag(tagName, baseCommitId); } catch (err) { setErrorState(err.message.replace(/^fatal:/, '')); Notification.update({ id, message: props.trans.__('Failed to create tag.'), type: 'error', autoClose: false }); return; } Notification.update({ id, message: props.trans.__('Tag created.'), type: 'success', autoClose: 5000 }); // Close the tag dialog: props.onClose(); setNameState(''); setFilterState(''); }; return (React.createElement(Dialog, { classes: { paper: branchDialogClass }, open: props.open, onClose: onClose }, React.createElement("div", { className: titleWrapperClass }, React.createElement("p", { className: titleClass }, props.trans.__('Create a Tag')), React.createElement("button", { className: closeButtonClass }, React.createElement(ClearIcon, { titleAccess: props.trans.__('Close this dialog'), fontSize: "small", onClick: onClose }))), React.createElement("div", { className: contentWrapperClass }, errorState ? React.createElement("p", { className: errorMessageClass }, errorState) : null, React.createElement("p", null, props.trans.__('Name')), React.createElement("input", { className: nameInputClass, type: "text", onChange: onNameChange, value: nameState, placeholder: "", title: props.trans.__('Enter a tag name') }), React.createElement("p", null, props.trans.__('Create tag pointing to…')), props.isSingleCommit ? null : (React.createElement("div", { className: filterWrapperClass }, React.createElement("div", { className: filterClass }, React.createElement("input", { className: filterInputClass, type: "text", onChange: onFilterChange, value: filterState, placeholder: props.trans.__('Filter by commit message'), title: props.trans.__('Filter history of commits menu') }), filterState ? (React.createElement("button", { className: filterClearClass }, React.createElement(ClearIcon, { titleAccess: props.trans.__('Clear the current filter'), fontSize: "small", onClick: resetFilter }))) : null))), React.createElement(DialogBoxCommitGraph, { pastCommits: props.pastCommits, model: props.model, trans: props.trans, filter: filterState, baseCommitId: baseCommitIdState, updateBaseCommitId: setBaseCommitIdState, isSingleCommit: props.isSingleCommit })), React.createElement(DialogActions, { className: actionsWrapperClass }, React.createElement("input", { className: classes(buttonClass, cancelButtonClass), type: "button", title: props.trans.__('Close this dialog without creating a new tag'), value: props.trans.__('Cancel'), onClick: onClose }), React.createElement("input", { className: classes(buttonClass, createButtonClass), type: "button", title: props.trans.__('Create a new tag'), value: props.trans.__('Create Tag'), onClick: () => { createTag(); }, disabled: nameState === '' || errorState !== '' })))); };