Skip to content

Commit

Permalink
Use preview tab for diff file views (#1184)
Browse files Browse the repository at this point in the history
* create PreviewMainAreaWidget extension class from MainAreaWidget that allows only one diff file open upon click

* fix typo

* find/get tab in addDiff cmd

* updt tabtitle fontstyle once clicked

* .

* clean PR and follow suggested advice

* clean code

* resolve all PR comments

* resolve PR comments
  • Loading branch information
sheezaaziz committed Dec 8, 2022
1 parent 2e8fd94 commit 55bb992
Show file tree
Hide file tree
Showing 3 changed files with 121 additions and 7 deletions.
39 changes: 32 additions & 7 deletions src/commandsAndMenu.tsx
Expand Up @@ -15,11 +15,11 @@ import { ISettingRegistry } from '@jupyterlab/settingregistry';
import { ITerminal } from '@jupyterlab/terminal';
import { ITranslator, TranslationBundle } from '@jupyterlab/translation';
import { closeIcon, ContextMenuSvg } from '@jupyterlab/ui-components';
import { ArrayExt, toArray } from '@lumino/algorithm';
import { ArrayExt, toArray, find } from '@lumino/algorithm';
import { CommandRegistry } from '@lumino/commands';
import { PromiseDelegate } from '@lumino/coreutils';
import { Message } from '@lumino/messaging';
import { ContextMenu, Menu, Panel, Widget } from '@lumino/widgets';
import { ContextMenu, DockPanel, Menu, Panel, Widget } from '@lumino/widgets';
import * as React from 'react';
import { DiffModel } from './components/diff/model';
import { createPlainTextDiff } from './components/diff/PlainTextDiff';
Expand Down Expand Up @@ -50,6 +50,7 @@ import { discardAllChanges } from './widgets/discardAllChanges';
import { ManageRemoteDialogue } from './components/ManageRemoteDialogue';
import { CheckboxForm } from './widgets/GitResetToRemoteForm';
import { AdvancedPushForm } from './widgets/AdvancedPushForm';
import { PreviewMainAreaWidget } from './components/diff/PreviewMainAreaWidget';

export interface IGitCloneArgs {
/**
Expand Down Expand Up @@ -87,6 +88,7 @@ interface IFileDiffArgument {
filePath: string;
isText: boolean;
status?: Git.Status;
isPreview?: boolean;

// when file has been relocated
previousFilePath?: string;
Expand Down Expand Up @@ -528,9 +530,10 @@ export function addCommands(
label: trans.__('Show Diff'),
caption: trans.__('Display a file diff.'),
execute: async args => {
const { model, isText } = args as any as {
const { model, isText, isPreview } = args as any as {
model: Git.Diff.IModel;
isText?: boolean;
isPreview?: boolean;
};

const fullPath = PathExt.join(
Expand All @@ -556,9 +559,10 @@ export function addCommands(
if (!mainAreaItem) {
const content = new Panel();
const modelIsLoading = new PromiseDelegate<void>();
const diffWidget = (mainAreaItem = new MainAreaWidget<Panel>({
const diffWidget = (mainAreaItem = new PreviewMainAreaWidget<Panel>({
content,
reveal: modelIsLoading.promise
reveal: modelIsLoading.promise,
isPreview
}));
diffWidget.id = id;
diffWidget.title.label = PathExt.basename(model.filename);
Expand All @@ -571,6 +575,19 @@ export function addCommands(
shell.add(diffWidget, 'main');
shell.activateById(diffWidget.id);

// Search for the tab
const dockPanel = (app.shell as any)._dockPanel as DockPanel;

// Get the index of the most recent tab opened
let tabPosition = -1;
const tabBar = find(dockPanel.tabBars(), bar => {
tabPosition = bar.titles.indexOf(diffWidget.title);
return tabPosition !== -1;
});

// Pin the preview screen if applicable
PreviewMainAreaWidget.pinWidget(tabPosition, tabBar, diffWidget);

// Create the diff widget
try {
const widget = await buildDiffWidget(
Expand Down Expand Up @@ -855,7 +872,14 @@ export function addCommands(
execute: async args => {
const { files } = args as any as CommandArguments.IGitFileDiff;
for (const file of files) {
const { context, filePath, previousFilePath, isText, status } = file;
const {
context,
filePath,
previousFilePath,
isText,
status,
isPreview
} = file;

// nothing to compare to for untracked files
if (status === 'untracked') {
Expand Down Expand Up @@ -967,7 +991,8 @@ export function addCommands(

const widget = await commands.execute(CommandIDs.gitShowDiff, {
model,
isText
isText,
isPreview
} as any);

if (widget) {
Expand Down
85 changes: 85 additions & 0 deletions src/components/diff/PreviewMainAreaWidget.ts
@@ -0,0 +1,85 @@
import { MainAreaWidget } from '@jupyterlab/apputils/lib/mainareawidget';
import { Message } from '@lumino/messaging';
import { Panel, TabBar, Widget } from '@lumino/widgets';

export class PreviewMainAreaWidget<
T extends Widget = Widget
> extends MainAreaWidget {
/**
* Handle on the preview widget
*/
protected static previewWidget: PreviewMainAreaWidget | null = null;

constructor(options: MainAreaWidget.IOptions<T> & { isPreview?: boolean }) {
super(options);

if (options.isPreview ?? true) {
PreviewMainAreaWidget.disposePreviewWidget(
PreviewMainAreaWidget.previewWidget
);
PreviewMainAreaWidget.previewWidget = this;
}
}

/**
* Dispose screen as a preview screen
*/
static disposePreviewWidget(isPreview: PreviewMainAreaWidget<Widget>): void {
return isPreview && PreviewMainAreaWidget.previewWidget.dispose();
}

/**
* Pin the preview screen if user clicks on tab title
*/
static pinWidget(
tabPosition: number,
tabBar: TabBar<Widget>,
diffWidget: PreviewMainAreaWidget<Panel>
): void {
// We need to wait for the tab node to be inserted in the DOM
setTimeout(() => {
// Get the most recent tab opened
const tab =
tabPosition >= 0 ? tabBar.contentNode.children[tabPosition] : null;
const tabTitle = tab.querySelector<HTMLElement>('.lm-TabBar-tabLabel');

tabTitle.classList.add('jp-git-tab-mod-preview');

const onClick = () => {
tabTitle.classList.remove('jp-git-tab-mod-preview');
tabTitle.removeEventListener('click', onClick, true);
if (PreviewMainAreaWidget.previewWidget === diffWidget) {
PreviewMainAreaWidget.previewWidget = null;
}
};

tabTitle.addEventListener('click', onClick, true);
diffWidget.disposed.connect(() => {
tabTitle.removeEventListener('click', onClick, true);
});
}, 0);
}

/**
* Callback just after the widget is attached to the DOM
*/
protected onAfterAttach(msg: Message): void {
super.onAfterAttach(msg);
this.node.addEventListener('click', this._onClick.bind(this), false);
}

/**
* Callback just before the widget is detached from the DOM
*/
protected onBeforeDetach(msg: Message): void {
this.node.removeEventListener('click', this._onClick.bind(this), false);
super.onBeforeAttach(msg);
}

/**
* Callback on click event in capture phase
*/
_onClick(): void {
PreviewMainAreaWidget.previewWidget = null;
}
}
4 changes: 4 additions & 0 deletions style/base.css
Expand Up @@ -10,3 +10,7 @@
@import url('variables.css');
@import url('status-widget.css');
@import url('advanced-push-form.css');

.jp-git-tab-mod-preview {
font-style: italic;
}

0 comments on commit 55bb992

Please sign in to comment.