mirror of
https://github.com/mathuo/dockview
synced 2025-03-09 23:42:05 +00:00
Merge branch 'master' of https://github.com/mathuo/dockview into 849-improved-dnd-overlay-model
This commit is contained in:
commit
3530c9896e
@ -2,7 +2,7 @@
|
|||||||
"packages": [
|
"packages": [
|
||||||
"packages/*"
|
"packages/*"
|
||||||
],
|
],
|
||||||
"version": "3.0.2",
|
"version": "3.2.0",
|
||||||
"npmClient": "yarn",
|
"npmClient": "yarn",
|
||||||
"command": {
|
"command": {
|
||||||
"publish": {
|
"publish": {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "dockview-angular",
|
"name": "dockview-angular",
|
||||||
"version": "3.0.2",
|
"version": "3.2.0",
|
||||||
"description": "Zero dependency layout manager supporting tabs, grids and splitviews",
|
"description": "Zero dependency layout manager supporting tabs, grids and splitviews",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"splitview",
|
"splitview",
|
||||||
@ -54,6 +54,6 @@
|
|||||||
"test:cov": "cross-env ../../node_modules/.bin/jest --selectProjects dockview --coverage"
|
"test:cov": "cross-env ../../node_modules/.bin/jest --selectProjects dockview --coverage"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"dockview-core": "^3.0.2"
|
"dockview-core": "^3.2.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "dockview-core",
|
"name": "dockview-core",
|
||||||
"version": "3.0.2",
|
"version": "3.2.0",
|
||||||
"description": "Zero dependency layout manager supporting tabs, grids and splitviews",
|
"description": "Zero dependency layout manager supporting tabs, grids and splitviews",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"splitview",
|
"splitview",
|
||||||
|
@ -1102,7 +1102,9 @@ describe('dockviewComponent', () => {
|
|||||||
disposable.dispose();
|
disposable.dispose();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('events flow', () => {
|
test('events flow', async () => {
|
||||||
|
window.open = () => setupMockWindow();
|
||||||
|
|
||||||
dockview.layout(1000, 1000);
|
dockview.layout(1000, 1000);
|
||||||
|
|
||||||
let events: {
|
let events: {
|
||||||
@ -1295,7 +1297,42 @@ describe('dockviewComponent', () => {
|
|||||||
expect(dockview.size).toBe(0);
|
expect(dockview.size).toBe(0);
|
||||||
expect(dockview.totalPanels).toBe(0);
|
expect(dockview.totalPanels).toBe(0);
|
||||||
|
|
||||||
|
events = [];
|
||||||
|
|
||||||
|
const panel8 = dockview.addPanel({
|
||||||
|
id: 'panel8',
|
||||||
|
component: 'default',
|
||||||
|
});
|
||||||
|
const panel9 = dockview.addPanel({
|
||||||
|
id: 'panel9',
|
||||||
|
component: 'default',
|
||||||
|
floating: true,
|
||||||
|
});
|
||||||
|
const panel10 = dockview.addPanel({
|
||||||
|
id: 'panel10',
|
||||||
|
component: 'default',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(await dockview.addPopoutGroup(panel10)).toBeTruthy();
|
||||||
|
|
||||||
|
expect(events).toEqual([
|
||||||
|
{ type: 'ADD_GROUP', group: panel8.group },
|
||||||
|
{ type: 'ADD_PANEL', panel: panel8 },
|
||||||
|
{ type: 'ACTIVE_GROUP', group: panel8.group },
|
||||||
|
{ type: 'ACTIVE_PANEL', panel: panel8 },
|
||||||
|
{ type: 'ADD_GROUP', group: panel9.group },
|
||||||
|
{ type: 'ADD_PANEL', panel: panel9 },
|
||||||
|
{ type: 'ACTIVE_GROUP', group: panel9.group },
|
||||||
|
{ type: 'ACTIVE_PANEL', panel: panel9 },
|
||||||
|
{ type: 'ADD_PANEL', panel: panel10 },
|
||||||
|
{ type: 'ACTIVE_PANEL', panel: panel10 },
|
||||||
|
{ type: 'ADD_GROUP', group: panel10.group },
|
||||||
|
]);
|
||||||
|
|
||||||
|
events = [];
|
||||||
disposable.dispose();
|
disposable.dispose();
|
||||||
|
|
||||||
|
expect(events.length).toBe(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('that removing a panel from a group reflects in the dockviewcomponent when searching for a panel', () => {
|
test('that removing a panel from a group reflects in the dockviewcomponent when searching for a panel', () => {
|
||||||
@ -5696,6 +5733,42 @@ describe('dockviewComponent', () => {
|
|||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('dispose of dockview instance when popup is open', async () => {
|
||||||
|
const container = document.createElement('div');
|
||||||
|
|
||||||
|
window.open = () => setupMockWindow();
|
||||||
|
|
||||||
|
const dockview = new DockviewComponent(container, {
|
||||||
|
createComponent(options) {
|
||||||
|
switch (options.name) {
|
||||||
|
case 'default':
|
||||||
|
return new PanelContentPartTest(
|
||||||
|
options.id,
|
||||||
|
options.name
|
||||||
|
);
|
||||||
|
default:
|
||||||
|
throw new Error(`unsupported`);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
dockview.layout(1000, 500);
|
||||||
|
|
||||||
|
dockview.addPanel({
|
||||||
|
id: 'panel_1',
|
||||||
|
component: 'default',
|
||||||
|
});
|
||||||
|
|
||||||
|
const panel2 = dockview.addPanel({
|
||||||
|
id: 'panel_2',
|
||||||
|
component: 'default',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(await dockview.addPopoutGroup(panel2.group)).toBeTruthy();
|
||||||
|
|
||||||
|
dockview.dispose();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('maximized group', () => {
|
describe('maximized group', () => {
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,8 +50,8 @@ export class Tab extends CompositeDisposable {
|
|||||||
private readonly dropTarget: Droptarget;
|
private readonly dropTarget: Droptarget;
|
||||||
private content: ITabRenderer | undefined = undefined;
|
private content: ITabRenderer | undefined = undefined;
|
||||||
|
|
||||||
private readonly _onChanged = new Emitter<MouseEvent>();
|
private readonly _onPointDown = new Emitter<MouseEvent>();
|
||||||
readonly onChanged: Event<MouseEvent> = this._onChanged.event;
|
readonly onPointerDown: Event<MouseEvent> = this._onPointDown.event;
|
||||||
|
|
||||||
private readonly _onDropped = new Emitter<DroptargetEvent>();
|
private readonly _onDropped = new Emitter<DroptargetEvent>();
|
||||||
readonly onDrop: Event<DroptargetEvent> = this._onDropped.event;
|
readonly onDrop: Event<DroptargetEvent> = this._onDropped.event;
|
||||||
@ -112,7 +112,7 @@ export class Tab extends CompositeDisposable {
|
|||||||
this.onWillShowOverlay = this.dropTarget.onWillShowOverlay;
|
this.onWillShowOverlay = this.dropTarget.onWillShowOverlay;
|
||||||
|
|
||||||
this.addDisposables(
|
this.addDisposables(
|
||||||
this._onChanged,
|
this._onPointDown,
|
||||||
this._onDropped,
|
this._onDropped,
|
||||||
this._onDragStart,
|
this._onDragStart,
|
||||||
dragHandler.onDragStart((event) => {
|
dragHandler.onDragStart((event) => {
|
||||||
@ -137,11 +137,7 @@ export class Tab extends CompositeDisposable {
|
|||||||
}),
|
}),
|
||||||
dragHandler,
|
dragHandler,
|
||||||
addDisposableListener(this._element, 'pointerdown', (event) => {
|
addDisposableListener(this._element, 'pointerdown', (event) => {
|
||||||
if (event.defaultPrevented) {
|
this._onPointDown.fire(event);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._onChanged.fire(event);
|
|
||||||
}),
|
}),
|
||||||
this.dropTarget.onDrop((event) => {
|
this.dropTarget.onDrop((event) => {
|
||||||
this._onDropped.fire(event);
|
this._onDropped.fire(event);
|
||||||
|
@ -286,6 +286,10 @@ export class TabsContainer
|
|||||||
|
|
||||||
const tabToRemove = this.tabs.splice(index, 1)[0];
|
const tabToRemove = this.tabs.splice(index, 1)[0];
|
||||||
|
|
||||||
|
if (!tabToRemove) {
|
||||||
|
throw new Error(`dockview: Tab not found`);
|
||||||
|
}
|
||||||
|
|
||||||
const { value, disposable } = tabToRemove;
|
const { value, disposable } = tabToRemove;
|
||||||
|
|
||||||
disposable.dispose();
|
disposable.dispose();
|
||||||
@ -316,7 +320,11 @@ export class TabsContainer
|
|||||||
tab.onDragStart((event) => {
|
tab.onDragStart((event) => {
|
||||||
this._onTabDragStart.fire({ nativeEvent: event, panel });
|
this._onTabDragStart.fire({ nativeEvent: event, panel });
|
||||||
}),
|
}),
|
||||||
tab.onChanged((event) => {
|
tab.onPointerDown((event) => {
|
||||||
|
if (event.defaultPrevented) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const isFloatingGroupsEnabled =
|
const isFloatingGroupsEnabled =
|
||||||
!this.accessor.options.disableFloatingGroups;
|
!this.accessor.options.disableFloatingGroups;
|
||||||
|
|
||||||
@ -345,14 +353,12 @@ 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) {
|
|
||||||
this.group.model.openPanel(panel);
|
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
tab.onDrop((event) => {
|
tab.onDrop((event) => {
|
||||||
|
@ -853,6 +853,10 @@ export class DockviewComponent
|
|||||||
),
|
),
|
||||||
overlayRenderContainer,
|
overlayRenderContainer,
|
||||||
Disposable.from(() => {
|
Disposable.from(() => {
|
||||||
|
if (this.isDisposed) {
|
||||||
|
return; // cleanup may run after instance is disposed
|
||||||
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
isGroupAddedToDom &&
|
isGroupAddedToDom &&
|
||||||
this.getPanel(referenceGroup.id)
|
this.getPanel(referenceGroup.id)
|
||||||
|
@ -118,15 +118,16 @@
|
|||||||
touch-action: none;
|
touch-action: none;
|
||||||
background-color: var(--dv-sash-color, transparent);
|
background-color: var(--dv-sash-color, transparent);
|
||||||
|
|
||||||
&:not(.disabled):active {
|
&:not(.disabled):active,
|
||||||
transition: background-color 0.1s ease-in-out;
|
|
||||||
background-color: var(--dv-active-sash-color, transparent);
|
|
||||||
}
|
|
||||||
|
|
||||||
&:not(.disabled):hover {
|
&:not(.disabled):hover {
|
||||||
background-color: var(--dv-active-sash-color, transparent);
|
background-color: var(--dv-active-sash-color, transparent);
|
||||||
transition: background-color 0.1s ease-in-out;
|
transition-property: background-color;
|
||||||
transition-delay: 0.5s;
|
transition-timing-function: ease-in-out;
|
||||||
|
transition-duration: var(
|
||||||
|
--dv-active-sash-transition-duration,
|
||||||
|
0.1s
|
||||||
|
);
|
||||||
|
transition-delay: var(--dv-active-sash-transition-delay, 0.5s);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,8 @@
|
|||||||
--dv-tab-margin: 0;
|
--dv-tab-margin: 0;
|
||||||
--dv-sash-color: transparent;
|
--dv-sash-color: transparent;
|
||||||
--dv-active-sash-color: transparent;
|
--dv-active-sash-color: transparent;
|
||||||
|
--dv-active-sash-transition-duration: 0.1s;
|
||||||
|
--dv-active-sash-transition-delay: 0.5s;
|
||||||
}
|
}
|
||||||
|
|
||||||
@mixin dockview-theme-dark-mixin {
|
@mixin dockview-theme-dark-mixin {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "dockview-react",
|
"name": "dockview-react",
|
||||||
"version": "3.0.2",
|
"version": "3.2.0",
|
||||||
"description": "Zero dependency layout manager supporting tabs, grids and splitviews",
|
"description": "Zero dependency layout manager supporting tabs, grids and splitviews",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"splitview",
|
"splitview",
|
||||||
@ -54,6 +54,6 @@
|
|||||||
"test:cov": "cross-env ../../node_modules/.bin/jest --selectProjects dockview-react --coverage"
|
"test:cov": "cross-env ../../node_modules/.bin/jest --selectProjects dockview-react --coverage"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"dockview": "^3.0.2"
|
"dockview": "^3.2.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "dockview-vue",
|
"name": "dockview-vue",
|
||||||
"version": "3.0.2",
|
"version": "3.2.0",
|
||||||
"description": "Zero dependency layout manager supporting tabs, grids and splitviews",
|
"description": "Zero dependency layout manager supporting tabs, grids and splitviews",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"splitview",
|
"splitview",
|
||||||
@ -52,7 +52,7 @@
|
|||||||
"test:cov": "cross-env ../../node_modules/.bin/jest --selectProjects dockview-vue --coverage"
|
"test:cov": "cross-env ../../node_modules/.bin/jest --selectProjects dockview-vue --coverage"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"dockview-core": "^3.0.2"
|
"dockview-core": "^3.2.0"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"vue": "^3.4.0"
|
"vue": "^3.4.0"
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "dockview",
|
"name": "dockview",
|
||||||
"version": "3.0.2",
|
"version": "3.2.0",
|
||||||
"description": "Zero dependency layout manager supporting tabs, grids and splitviews",
|
"description": "Zero dependency layout manager supporting tabs, grids and splitviews",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"splitview",
|
"splitview",
|
||||||
@ -54,7 +54,7 @@
|
|||||||
"test:cov": "cross-env ../../node_modules/.bin/jest --selectProjects dockview --coverage"
|
"test:cov": "cross-env ../../node_modules/.bin/jest --selectProjects dockview --coverage"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"dockview-core": "^3.0.2"
|
"dockview-core": "^3.2.0"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||||
|
@ -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);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
@ -32,10 +32,15 @@ export const DockviewDefaultTab: React.FunctionComponent<
|
|||||||
params: _params,
|
params: _params,
|
||||||
hideClose,
|
hideClose,
|
||||||
closeActionOverride,
|
closeActionOverride,
|
||||||
|
onPointerDown,
|
||||||
|
onPointerUp,
|
||||||
|
onPointerLeave,
|
||||||
...rest
|
...rest
|
||||||
}) => {
|
}) => {
|
||||||
const title = useTitle(api);
|
const title = useTitle(api);
|
||||||
|
|
||||||
|
const isMiddleMouseButton = React.useRef<boolean>(false);
|
||||||
|
|
||||||
const onClose = React.useCallback(
|
const onClose = React.useCallback(
|
||||||
(event: React.MouseEvent<HTMLSpanElement>) => {
|
(event: React.MouseEvent<HTMLSpanElement>) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
@ -49,37 +54,52 @@ export const DockviewDefaultTab: React.FunctionComponent<
|
|||||||
[api, closeActionOverride]
|
[api, closeActionOverride]
|
||||||
);
|
);
|
||||||
|
|
||||||
const onPointerDown = React.useCallback((e: React.MouseEvent) => {
|
const onBtnPointerDown = React.useCallback((event: React.MouseEvent) => {
|
||||||
e.preventDefault();
|
event.preventDefault();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const onClick = React.useCallback(
|
const _onPointerDown = React.useCallback(
|
||||||
(event: React.MouseEvent<HTMLDivElement>) => {
|
(event: React.PointerEvent<HTMLDivElement>) => {
|
||||||
if (event.defaultPrevented) {
|
isMiddleMouseButton.current = event.button === 1;
|
||||||
return;
|
onPointerDown?.(event);
|
||||||
}
|
|
||||||
|
|
||||||
api.setActive();
|
|
||||||
|
|
||||||
if (rest.onClick) {
|
|
||||||
rest.onClick(event);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
[api, rest.onClick]
|
[onPointerDown]
|
||||||
|
);
|
||||||
|
|
||||||
|
const _onPointerUp = React.useCallback(
|
||||||
|
(event: React.PointerEvent<HTMLDivElement>) => {
|
||||||
|
if (isMiddleMouseButton && event.button === 1 && !hideClose) {
|
||||||
|
isMiddleMouseButton.current = false;
|
||||||
|
onClose(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
onPointerUp?.(event);
|
||||||
|
},
|
||||||
|
[onPointerUp, onClose, hideClose]
|
||||||
|
);
|
||||||
|
|
||||||
|
const _onPointerLeave = React.useCallback(
|
||||||
|
(event: React.PointerEvent<HTMLDivElement>) => {
|
||||||
|
isMiddleMouseButton.current = false;
|
||||||
|
onPointerLeave?.(event);
|
||||||
|
},
|
||||||
|
[onPointerLeave]
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
data-testid="dockview-dv-default-tab"
|
data-testid="dockview-dv-default-tab"
|
||||||
{...rest}
|
{...rest}
|
||||||
onClick={onClick}
|
onPointerDown={_onPointerDown}
|
||||||
|
onPointerUp={_onPointerUp}
|
||||||
|
onPointerLeave={_onPointerLeave}
|
||||||
className="dv-default-tab"
|
className="dv-default-tab"
|
||||||
>
|
>
|
||||||
<span className="dv-default-tab-content">{title}</span>
|
<span className="dv-default-tab-content">{title}</span>
|
||||||
{!hideClose && (
|
{!hideClose && (
|
||||||
<div
|
<div
|
||||||
className="dv-default-tab-action"
|
className="dv-default-tab-action"
|
||||||
onPointerDown={onPointerDown}
|
onPointerDown={onBtnPointerDown}
|
||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
>
|
>
|
||||||
<CloseButton />
|
<CloseButton />
|
||||||
|
22
packages/docs/blog/2025-02-02-dockview-3.1.0.md
Normal file
22
packages/docs/blog/2025-02-02-dockview-3.1.0.md
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
---
|
||||||
|
slug: dockview-3.1.0-release
|
||||||
|
title: Dockview 3.1.0
|
||||||
|
tags: [release]
|
||||||
|
---
|
||||||
|
|
||||||
|
# Release Notes
|
||||||
|
|
||||||
|
Please reference docs @ [dockview.dev](https://dockview.dev).
|
||||||
|
|
||||||
|
## 🚀 Features
|
||||||
|
|
||||||
|
- Close tab with middle mouse button [#847](https://github.com/mathuo/dockview/pull/847)
|
||||||
|
|
||||||
|
## 🛠 Miscs
|
||||||
|
|
||||||
|
- Bug: Fix crash on navigation with open popout group [#835](https://github.com/mathuo/dockview/pull/848) [#845](https://github.com/mathuo/dockview/pull/845)
|
||||||
|
- Bug: Subscribe to `onDidAcitvePanelChange` immediately, rather than deferred to `queueMicrotask` [#843](https://github.com/mathuo/dockview/pull/843)
|
||||||
|
- Bug: Minor theme fixup [#831](https://github.com/mathuo/dockview/pull/831)
|
||||||
|
|
||||||
|
## 🔥 Breaking changes
|
||||||
|
|
18
packages/docs/blog/2025-02-09-dockview-3.1.1.md
Normal file
18
packages/docs/blog/2025-02-09-dockview-3.1.1.md
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
---
|
||||||
|
slug: dockview-3.1.1-release
|
||||||
|
title: Dockview 3.1.1
|
||||||
|
tags: [release]
|
||||||
|
---
|
||||||
|
|
||||||
|
# Release Notes
|
||||||
|
|
||||||
|
Please reference docs @ [dockview.dev](https://dockview.dev).
|
||||||
|
|
||||||
|
## 🚀 Features
|
||||||
|
|
||||||
|
## 🛠 Miscs
|
||||||
|
|
||||||
|
- Bug: Fix Middle mouse button to close tab [#835](https://github.com/mathuo/dockview/issues/853)
|
||||||
|
|
||||||
|
## 🔥 Breaking changes
|
||||||
|
|
18
packages/docs/blog/2025-02-12-dockview-3.2.0.md
Normal file
18
packages/docs/blog/2025-02-12-dockview-3.2.0.md
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
---
|
||||||
|
slug: dockview-3.2.0-release
|
||||||
|
title: Dockview 3.2.0
|
||||||
|
tags: [release]
|
||||||
|
---
|
||||||
|
|
||||||
|
# Release Notes
|
||||||
|
|
||||||
|
Please reference docs @ [dockview.dev](https://dockview.dev).
|
||||||
|
|
||||||
|
## 🚀 Features
|
||||||
|
|
||||||
|
- Add CSS properties `--dv-active-sash-transition-duration` and `--dv-active-sash-transition-delay` [#835](https://github.com/mathuo/dockview/issues/859)
|
||||||
|
|
||||||
|
## 🛠 Miscs
|
||||||
|
|
||||||
|
## 🔥 Breaking changes
|
||||||
|
|
@ -184,17 +184,8 @@ const config = {
|
|||||||
label: 'API',
|
label: 'API',
|
||||||
},
|
},
|
||||||
{ to: '/blog', label: 'Blog', position: 'left' },
|
{ to: '/blog', label: 'Blog', position: 'left' },
|
||||||
|
{ href: '/templates', target:"_blank", label: 'Examples', position: 'left' },
|
||||||
{ to: '/demo', label: 'Demo', position: 'left' },
|
{ to: '/demo', label: 'Demo', position: 'left' },
|
||||||
// {
|
|
||||||
// to: 'https://dockview.dev/typedocs',
|
|
||||||
// label: 'TSDoc',
|
|
||||||
// position: 'left',
|
|
||||||
// },
|
|
||||||
|
|
||||||
// {
|
|
||||||
// type: 'docsVersionDropdown',
|
|
||||||
// position: 'right',
|
|
||||||
// },
|
|
||||||
{
|
{
|
||||||
href: 'https://github.com/mathuo/dockview',
|
href: 'https://github.com/mathuo/dockview',
|
||||||
position: 'right',
|
position: 'right',
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "dockview-docs",
|
"name": "dockview-docs",
|
||||||
"version": "3.0.2",
|
"version": "3.2.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "npm run build-templates && docusaurus build",
|
"build": "npm run build-templates && docusaurus build",
|
||||||
@ -38,7 +38,7 @@
|
|||||||
"ag-grid-react": "^31.0.2",
|
"ag-grid-react": "^31.0.2",
|
||||||
"axios": "^1.6.3",
|
"axios": "^1.6.3",
|
||||||
"clsx": "^2.1.0",
|
"clsx": "^2.1.0",
|
||||||
"dockview": "^3.0.2",
|
"dockview": "^3.2.0",
|
||||||
"prism-react-renderer": "^2.3.1",
|
"prism-react-renderer": "^2.3.1",
|
||||||
"react-dnd": "^16.0.1",
|
"react-dnd": "^16.0.1",
|
||||||
"react-laag": "^2.0.5",
|
"react-laag": "^2.0.5",
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import fs from 'fs-extra';
|
import fs from 'fs-extra';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import { argv } from 'process';
|
import { argv } from 'process';
|
||||||
import { execSync } from 'child_process';
|
|
||||||
|
|
||||||
import { fileURLToPath } from 'url';
|
import { fileURLToPath } from 'url';
|
||||||
|
|
||||||
@ -86,7 +85,8 @@ function createIndexHTML(options) {
|
|||||||
.map(([key, value]) => `"${key}": "${value}"`)
|
.map(([key, value]) => `"${key}": "${value}"`)
|
||||||
.join(',\n')}`
|
.join(',\n')}`
|
||||||
)
|
)
|
||||||
.replace('{{app}}', options.app);
|
.replace('{{app}}', options.app)
|
||||||
|
.replace('{{githubLink}}', options.githubUrl)
|
||||||
}
|
}
|
||||||
|
|
||||||
const input_dir = path.join(__dirname, '../templates');
|
const input_dir = path.join(__dirname, '../templates');
|
||||||
@ -97,6 +97,8 @@ const FRAMEWORKS = ['react', 'vue', 'typescript'];
|
|||||||
|
|
||||||
const list = [];
|
const list = [];
|
||||||
|
|
||||||
|
const githubUrl = "https://github.com/mathuo/dockview/tree/master/packages/docs/templates"
|
||||||
|
|
||||||
for (const component of COMPONENTS) {
|
for (const component of COMPONENTS) {
|
||||||
const componentDir = path.join(input_dir, component);
|
const componentDir = path.join(input_dir, component);
|
||||||
|
|
||||||
@ -115,6 +117,9 @@ for (const component of COMPONENTS) {
|
|||||||
path.join(componentDir, folder, framework, 'src'),
|
path.join(componentDir, folder, framework, 'src'),
|
||||||
path.join(output, component, folder, framework, 'src')
|
path.join(output, component, folder, framework, 'src')
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const templateGithubUrl = `${githubUrl}/${component}/${folder}/${framework}/src`
|
||||||
|
|
||||||
const template = createIndexHTML({
|
const template = createIndexHTML({
|
||||||
title: `Dockview | ${folder} ${framework}`,
|
title: `Dockview | ${folder} ${framework}`,
|
||||||
app:
|
app:
|
||||||
@ -127,6 +132,7 @@ for (const component of COMPONENTS) {
|
|||||||
USE_LOCAL_CDN ? 'local' : 'remote'
|
USE_LOCAL_CDN ? 'local' : 'remote'
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
githubUrl: templateGithubUrl
|
||||||
});
|
});
|
||||||
fs.writeFileSync(
|
fs.writeFileSync(
|
||||||
path.join(output, component, folder, framework, 'index.html'),
|
path.join(output, component, folder, framework, 'index.html'),
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
<style media="only screen">
|
<style media="only screen">
|
||||||
html,
|
html,
|
||||||
body,
|
body,
|
||||||
#app {
|
#root {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
@ -22,6 +22,26 @@
|
|||||||
BlinkMacSystemFont, Segoe UI, Roboto;
|
BlinkMacSystemFont, Segoe UI, Roboto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#header {
|
||||||
|
height: 25px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#header-btn {
|
||||||
|
height: 22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#gh-logo {
|
||||||
|
height: 22px;
|
||||||
|
width: 22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#app {
|
||||||
|
height: calc(100% - 25px);
|
||||||
|
}
|
||||||
|
|
||||||
html {
|
html {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
@ -31,7 +51,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
padding: 16px;
|
padding: 8px;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
@ -62,9 +82,18 @@
|
|||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<div id="app">
|
<div id="root">
|
||||||
<script type="systemjs-module" src="import:{{app}}"></script>
|
<div id="header">
|
||||||
|
<a target="_blank" rel="noopener noreferrer" href="{{githubLink}}">
|
||||||
|
<button id="header-btn">
|
||||||
|
View Source
|
||||||
|
</button>
|
||||||
|
</a>
|
||||||
|
<img id="gh-logo" src="https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png"/>
|
||||||
|
</div>
|
||||||
|
<div id="app"></div>
|
||||||
</div>
|
</div>
|
||||||
|
<script type="systemjs-module" src="import:{{app}}"></script>
|
||||||
<object
|
<object
|
||||||
id="loading-spinner"
|
id="loading-spinner"
|
||||||
style="
|
style="
|
||||||
|
23
packages/docs/templates/dockview/group-actions/typescript/src/index.css
vendored
Normal file
23
packages/docs/templates/dockview/group-actions/typescript/src/index.css
vendored
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
.dockview-groupcontrol-demo {
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
color: white;
|
||||||
|
background-color: black;
|
||||||
|
padding: 0px 8px;
|
||||||
|
|
||||||
|
margin: 1px;
|
||||||
|
border: 1px dotted orange;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.dockview-groupcontrol-demo .dockview-groupcontrol-demo-group-active {
|
||||||
|
padding: 0px 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dockview-groupcontrol-demo .dockview-groupcontrol-demo-active-panel {
|
||||||
|
color: yellow;
|
||||||
|
padding: 0px 8px;
|
||||||
|
}
|
||||||
|
|
178
packages/docs/templates/dockview/group-actions/typescript/src/index.ts
vendored
Normal file
178
packages/docs/templates/dockview/group-actions/typescript/src/index.ts
vendored
Normal file
@ -0,0 +1,178 @@
|
|||||||
|
import 'dockview-core/dist/styles/dockview.css';
|
||||||
|
import {
|
||||||
|
createDockview,
|
||||||
|
DockviewGroupPanel,
|
||||||
|
GroupPanelPartInitParameters,
|
||||||
|
IContentRenderer,
|
||||||
|
IGroupHeaderProps,
|
||||||
|
IHeaderActionsRenderer,
|
||||||
|
} from 'dockview-core';
|
||||||
|
import './index.css';
|
||||||
|
|
||||||
|
class Panel implements IContentRenderer {
|
||||||
|
private readonly _element: HTMLElement;
|
||||||
|
|
||||||
|
get element(): HTMLElement {
|
||||||
|
return this._element;
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this._element = document.createElement('div');
|
||||||
|
this._element.style.display = 'flex';
|
||||||
|
this._element.style.justifyContent = 'center';
|
||||||
|
this._element.style.alignItems = 'center';
|
||||||
|
this._element.style.color = 'gray';
|
||||||
|
this._element.style.height = '100%';
|
||||||
|
}
|
||||||
|
|
||||||
|
init(parameters: GroupPanelPartInitParameters): void {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class PrefixHeader implements IHeaderActionsRenderer {
|
||||||
|
private readonly _element: HTMLElement;
|
||||||
|
|
||||||
|
get element(): HTMLElement {
|
||||||
|
return this._element;
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(group: DockviewGroupPanel) {
|
||||||
|
this._element = document.createElement('div');
|
||||||
|
this._element.className = 'dockview-groupcontrol-demo';
|
||||||
|
this._element.innerText = '🌲';
|
||||||
|
}
|
||||||
|
|
||||||
|
init(parameters: IGroupHeaderProps): void {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
dispose(): void {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class RightHeaderActions implements IHeaderActionsRenderer {
|
||||||
|
private readonly _element: HTMLElement;
|
||||||
|
private readonly _disposables: (() => void)[] = [];
|
||||||
|
|
||||||
|
get element(): HTMLElement {
|
||||||
|
return this._element;
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(group: DockviewGroupPanel) {
|
||||||
|
this._element = document.createElement('div');
|
||||||
|
this._element.className = 'dockview-groupcontrol-demo';
|
||||||
|
}
|
||||||
|
|
||||||
|
init(parameters: IGroupHeaderProps): void {
|
||||||
|
const group = parameters.group;
|
||||||
|
|
||||||
|
const span = document.createElement('span');
|
||||||
|
span.className = 'dockview-groupcontrol-demo-group-active';
|
||||||
|
|
||||||
|
this._element.appendChild(span);
|
||||||
|
|
||||||
|
const d1 = group.api.onDidActiveChange(() => {
|
||||||
|
span.style.background = group.api.isActive ? 'green' : 'red';
|
||||||
|
span.innerText = `${
|
||||||
|
group.api.isActive ? 'Group Active' : 'Group Inactive'
|
||||||
|
}`;
|
||||||
|
});
|
||||||
|
|
||||||
|
span.style.background = group.api.isActive ? 'green' : 'red';
|
||||||
|
span.innerText = `${
|
||||||
|
group.api.isActive ? 'Group Active' : 'Group Inactive'
|
||||||
|
}`;
|
||||||
|
|
||||||
|
this._disposables.push(() => d1.dispose());
|
||||||
|
}
|
||||||
|
|
||||||
|
dispose(): void {
|
||||||
|
this._disposables.forEach((dispose) => dispose());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class LeftHeaderActions implements IHeaderActionsRenderer {
|
||||||
|
private readonly _element: HTMLElement;
|
||||||
|
private readonly _disposables: (() => void)[] = [];
|
||||||
|
|
||||||
|
get element(): HTMLElement {
|
||||||
|
return this._element;
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(group: DockviewGroupPanel) {
|
||||||
|
console.log('group', group);
|
||||||
|
this._element = document.createElement('div');
|
||||||
|
this._element.className = 'dockview-groupcontrol-demo';
|
||||||
|
}
|
||||||
|
|
||||||
|
init(parameters: IGroupHeaderProps): void {
|
||||||
|
const group = parameters.group;
|
||||||
|
|
||||||
|
const span = document.createElement('span');
|
||||||
|
span.className = 'dockview-groupcontrol-demo-active-panel';
|
||||||
|
|
||||||
|
this._element.appendChild(span);
|
||||||
|
|
||||||
|
const d1 = group.api.onDidActivePanelChange((event) => {
|
||||||
|
console.log('event', event);
|
||||||
|
span.innerText = `activePanel: ${event.panel?.id || 'null'}`;
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('group.activePanel', group.activePanel);
|
||||||
|
|
||||||
|
span.innerText = `activePanel: ${group.activePanel?.id || 'null'}`;
|
||||||
|
|
||||||
|
this._disposables.push(() => d1.dispose());
|
||||||
|
}
|
||||||
|
|
||||||
|
dispose(): void {
|
||||||
|
this._disposables.forEach((dispose) => dispose());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const api = createDockview(document.getElementById('app'), {
|
||||||
|
className: 'dockview-theme-abyss',
|
||||||
|
createComponent: (options): IContentRenderer => {
|
||||||
|
switch (options.name) {
|
||||||
|
case 'default':
|
||||||
|
return new Panel();
|
||||||
|
default:
|
||||||
|
throw new Error('Panel not found');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
createPrefixHeaderActionComponent: (group): IHeaderActionsRenderer => {
|
||||||
|
return new PrefixHeader(group);
|
||||||
|
},
|
||||||
|
createLeftHeaderActionComponent: (group): IHeaderActionsRenderer => {
|
||||||
|
return new LeftHeaderActions(group);
|
||||||
|
},
|
||||||
|
createRightHeaderActionComponent: (group): IHeaderActionsRenderer => {
|
||||||
|
return new RightHeaderActions(group);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
api.addPanel({
|
||||||
|
id: 'panel_1',
|
||||||
|
component: 'default',
|
||||||
|
title: 'Panel 1',
|
||||||
|
});
|
||||||
|
|
||||||
|
api.addPanel({
|
||||||
|
id: 'panel_2',
|
||||||
|
component: 'default',
|
||||||
|
title: 'Panel 2',
|
||||||
|
position: {
|
||||||
|
direction: 'right',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
api.addPanel({
|
||||||
|
id: 'panel_3',
|
||||||
|
component: 'default',
|
||||||
|
title: 'Panel 3',
|
||||||
|
position: {
|
||||||
|
direction: 'below',
|
||||||
|
},
|
||||||
|
});
|
Loading…
Reference in New Issue
Block a user