feat: close tab with middle btn

This commit is contained in:
mathuo 2025-01-30 13:43:53 +00:00
parent bb93c9e4c8
commit 0e9c648fa9
No known key found for this signature in database
GPG Key ID: C6EEDEFD6CA07281
4 changed files with 31 additions and 64 deletions

View File

@ -29,12 +29,6 @@ export class DefaultTab extends CompositeDisposable implements ITabRenderer {
this._element.appendChild(this._content); this._element.appendChild(this._content);
this._element.appendChild(this.action); this._element.appendChild(this.action);
this.addDisposables(
addDisposableListener(this.action, 'pointerdown', (ev) => {
ev.preventDefault();
})
);
this.render(); this.render();
} }

View File

@ -314,6 +314,10 @@ export class TabsContainer
this._onTabDragStart.fire({ nativeEvent: event, panel }); this._onTabDragStart.fire({ nativeEvent: event, panel });
}), }),
tab.onChanged((event) => { tab.onChanged((event) => {
if (event.defaultPrevented) {
return;
}
const isFloatingGroupsEnabled = const isFloatingGroupsEnabled =
!this.accessor.options.disableFloatingGroups; !this.accessor.options.disableFloatingGroups;
@ -342,14 +346,15 @@ export class TabsContainer
return; return;
} }
const isLeftClick = event.button === 0; switch (event.button) {
case 0: // left click or touch
if (!isLeftClick || event.defaultPrevented) { if (this.group.activePanel !== panel) {
return; this.group.model.openPanel(panel);
} }
break;
if (this.group.activePanel !== panel) { case 1: // middle click
this.group.model.openPanel(panel); panel.api.close();
break;
} }
}), }),
tab.onDrop((event) => { tab.onDrop((event) => {

View File

@ -10,7 +10,9 @@ import { Disposable } from 'dockview-core/dist/cjs/lifecycle';
describe('defaultTab', () => { describe('defaultTab', () => {
test('has close button by default', async () => { test('has close button by default', async () => {
const api = fromPartial<DockviewPanelApi>({ const api = fromPartial<DockviewPanelApi>({
onDidTitleChange: jest.fn().mockImplementation(() => Disposable.NONE), onDidTitleChange: jest
.fn()
.mockImplementation(() => Disposable.NONE),
}); });
const containerApi = fromPartial<DockviewApi>({}); const containerApi = fromPartial<DockviewApi>({});
const params = {}; const params = {};
@ -30,7 +32,9 @@ describe('defaultTab', () => {
test('that title is displayed', async () => { test('that title is displayed', async () => {
const api = fromPartial<DockviewPanelApi>({ const api = fromPartial<DockviewPanelApi>({
title: 'test_title', title: 'test_title',
onDidTitleChange: jest.fn().mockImplementation(() => Disposable.NONE), onDidTitleChange: jest
.fn()
.mockImplementation(() => Disposable.NONE),
}); });
const containerApi = fromPartial<DockviewApi>({}); const containerApi = fromPartial<DockviewApi>({});
const params = {}; const params = {};
@ -84,7 +88,9 @@ describe('defaultTab', () => {
test('has no close button when hideClose=true', async () => { test('has no close button when hideClose=true', async () => {
const api = fromPartial<DockviewPanelApi>({ const api = fromPartial<DockviewPanelApi>({
onDidTitleChange: jest.fn().mockImplementation(() => Disposable.NONE), onDidTitleChange: jest
.fn()
.mockImplementation(() => Disposable.NONE),
}); });
const containerApi = fromPartial<DockviewApi>({}); const containerApi = fromPartial<DockviewApi>({});
const params = {}; const params = {};
@ -105,7 +111,9 @@ describe('defaultTab', () => {
test('that settings closeActionOverride skips api.close()', async () => { test('that settings closeActionOverride skips api.close()', async () => {
const api = fromPartial<DockviewPanelApi>({ const api = fromPartial<DockviewPanelApi>({
close: jest.fn(), close: jest.fn(),
onDidTitleChange: jest.fn().mockImplementation(() => Disposable.NONE), onDidTitleChange: jest
.fn()
.mockImplementation(() => Disposable.NONE),
}); });
const containerApi = fromPartial<DockviewApi>({}); const containerApi = fromPartial<DockviewApi>({});
const params = {}; const params = {};
@ -134,7 +142,9 @@ describe('defaultTab', () => {
test('that clicking close calls api.close()', async () => { test('that clicking close calls api.close()', async () => {
const api = fromPartial<DockviewPanelApi>({ const api = fromPartial<DockviewPanelApi>({
close: jest.fn(), close: jest.fn(),
onDidTitleChange: jest.fn().mockImplementation(() => Disposable.NONE), onDidTitleChange: jest
.fn()
.mockImplementation(() => Disposable.NONE),
}); });
const containerApi = fromPartial<DockviewApi>({}); const containerApi = fromPartial<DockviewApi>({});
const params = {}; const params = {};
@ -158,7 +168,9 @@ describe('defaultTab', () => {
test('has close button when hideClose=false', async () => { test('has close button when hideClose=false', async () => {
const api = fromPartial<DockviewPanelApi>({ const api = fromPartial<DockviewPanelApi>({
onDidTitleChange: jest.fn().mockImplementation(() => Disposable.NONE), onDidTitleChange: jest
.fn()
.mockImplementation(() => Disposable.NONE),
}); });
const containerApi = fromPartial<DockviewApi>({}); const containerApi = fromPartial<DockviewApi>({});
const params = {}; const params = {};
@ -175,32 +187,4 @@ describe('defaultTab', () => {
const element = await screen.getByTestId('dockview-dv-default-tab'); const element = await screen.getByTestId('dockview-dv-default-tab');
expect(element.querySelector('.dv-default-tab-action')).toBeTruthy(); expect(element.querySelector('.dv-default-tab-action')).toBeTruthy();
}); });
test('that pointerDown on close button prevents panel becoming active', async () => {
const api = fromPartial<DockviewPanelApi>({
setActive: jest.fn(),
onDidTitleChange: jest.fn().mockImplementation(() => Disposable.NONE),
});
const containerApi = fromPartial<DockviewApi>({});
const params = {};
render(
<DockviewDefaultTab
api={api}
containerApi={containerApi}
params={params}
/>
);
const element = await screen.getByTestId('dockview-dv-default-tab');
const btn = element.querySelector(
'.dv-default-tab-action'
) as HTMLElement;
fireEvent.pointerDown(btn);
expect(api.setActive).toHaveBeenCalledTimes(0);
fireEvent.click(element);
expect(api.setActive).toHaveBeenCalledTimes(1);
});
}); });

View File

@ -53,26 +53,10 @@ export const DockviewDefaultTab: React.FunctionComponent<
e.preventDefault(); e.preventDefault();
}, []); }, []);
const onClick = React.useCallback(
(event: React.MouseEvent<HTMLDivElement>) => {
if (event.defaultPrevented) {
return;
}
api.setActive();
if (rest.onClick) {
rest.onClick(event);
}
},
[api, rest.onClick]
);
return ( return (
<div <div
data-testid="dockview-dv-default-tab" data-testid="dockview-dv-default-tab"
{...rest} {...rest}
onClick={onClick}
className="dv-default-tab" className="dv-default-tab"
> >
<span className="dv-default-tab-content">{title}</span> <span className="dv-default-tab-content">{title}</span>