mirror of
https://github.com/mathuo/dockview
synced 2025-05-12 14:38:25 +00:00
feat: use svg icons and CSS color property for icons
This commit is contained in:
parent
68e573e5f0
commit
0c28f2dabe
@ -75,7 +75,7 @@ class PanelTabPartTest implements ITabRenderer {
|
|||||||
isDisposed: boolean = false;
|
isDisposed: boolean = false;
|
||||||
|
|
||||||
constructor(public readonly id: string, component: string) {
|
constructor(public readonly id: string, component: string) {
|
||||||
this.element.classList.add(`testpanel-${id}`);
|
this.element.className = `panel-tab-part-${id}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
updateParentGroup(group: GroupPanel, isPanelVisible: boolean): void {
|
updateParentGroup(group: GroupPanel, isPanelVisible: boolean): void {
|
||||||
@ -1686,7 +1686,293 @@ describe('dockviewComponent', () => {
|
|||||||
return disposable.dispose();
|
return disposable.dispose();
|
||||||
});
|
});
|
||||||
|
|
||||||
// group is disposed of when dockview is disposed
|
test('load a layout with a non-existant tab id', () => {
|
||||||
// watermark is disposed of when removed
|
const container = document.createElement('div');
|
||||||
// watermark is disposed of when dockview is disposed
|
|
||||||
|
const dockview = new DockviewComponent(container, {
|
||||||
|
components: {
|
||||||
|
default: PanelContentPartTest,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
dockview.deserializer = new ReactPanelDeserialzier(dockview);
|
||||||
|
dockview.fromJSON({
|
||||||
|
activeGroup: 'group-1',
|
||||||
|
grid: {
|
||||||
|
root: {
|
||||||
|
type: 'branch',
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
type: 'leaf',
|
||||||
|
data: {
|
||||||
|
views: ['panel1'],
|
||||||
|
id: 'group-1',
|
||||||
|
activeView: 'panel1',
|
||||||
|
},
|
||||||
|
size: 500,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'branch',
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
type: 'leaf',
|
||||||
|
data: {
|
||||||
|
views: ['panel2', 'panel3'],
|
||||||
|
id: 'group-2',
|
||||||
|
},
|
||||||
|
size: 500,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'leaf',
|
||||||
|
data: { views: ['panel4'], id: 'group-3' },
|
||||||
|
size: 500,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
size: 250,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'leaf',
|
||||||
|
data: { views: ['panel5'], id: 'group-4' },
|
||||||
|
size: 250,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
size: 1000,
|
||||||
|
},
|
||||||
|
height: 1000,
|
||||||
|
width: 1000,
|
||||||
|
orientation: Orientation.VERTICAL,
|
||||||
|
},
|
||||||
|
panels: {
|
||||||
|
panel1: {
|
||||||
|
id: 'panel1',
|
||||||
|
view: { content: { id: 'default' } },
|
||||||
|
title: 'panel1',
|
||||||
|
},
|
||||||
|
panel2: {
|
||||||
|
id: 'panel2',
|
||||||
|
view: {
|
||||||
|
content: { id: 'default' },
|
||||||
|
tab: { id: '__non__existant_tab__' },
|
||||||
|
},
|
||||||
|
title: 'panel2',
|
||||||
|
},
|
||||||
|
panel3: {
|
||||||
|
id: 'panel3',
|
||||||
|
view: { content: { id: 'default' } },
|
||||||
|
title: 'panel3',
|
||||||
|
},
|
||||||
|
panel4: {
|
||||||
|
id: 'panel4',
|
||||||
|
view: { content: { id: 'default' } },
|
||||||
|
title: 'panel4',
|
||||||
|
},
|
||||||
|
panel5: {
|
||||||
|
id: 'panel5',
|
||||||
|
view: { content: { id: 'default' } },
|
||||||
|
title: 'panel5',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
options: { tabHeight: 25 },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('load and persist layout with custom tab header', () => {
|
||||||
|
const container = document.createElement('div');
|
||||||
|
|
||||||
|
const dockview = new DockviewComponent(container, {
|
||||||
|
components: {
|
||||||
|
default: PanelContentPartTest,
|
||||||
|
},
|
||||||
|
tabComponents: {
|
||||||
|
test_tab_id: PanelTabPartTest,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
dockview.deserializer = new ReactPanelDeserialzier(dockview);
|
||||||
|
dockview.fromJSON({
|
||||||
|
activeGroup: 'group-1',
|
||||||
|
grid: {
|
||||||
|
root: {
|
||||||
|
type: 'branch',
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
type: 'leaf',
|
||||||
|
data: {
|
||||||
|
views: ['panel1'],
|
||||||
|
id: 'group-1',
|
||||||
|
activeView: 'panel1',
|
||||||
|
},
|
||||||
|
size: 500,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'leaf',
|
||||||
|
data: {
|
||||||
|
views: ['panel2'],
|
||||||
|
id: 'group-2',
|
||||||
|
activeView: 'panel2',
|
||||||
|
},
|
||||||
|
|
||||||
|
size: 500,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
size: 1000,
|
||||||
|
},
|
||||||
|
height: 1000,
|
||||||
|
width: 1000,
|
||||||
|
orientation: Orientation.VERTICAL,
|
||||||
|
},
|
||||||
|
panels: {
|
||||||
|
panel1: {
|
||||||
|
id: 'panel1',
|
||||||
|
view: { content: { id: 'default' } },
|
||||||
|
title: 'panel1',
|
||||||
|
},
|
||||||
|
panel2: {
|
||||||
|
id: 'panel2',
|
||||||
|
view: {
|
||||||
|
content: { id: 'default' },
|
||||||
|
tab: { id: 'test_tab_id' },
|
||||||
|
},
|
||||||
|
title: 'panel2',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
options: { tabHeight: 25 },
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(JSON.parse(JSON.stringify(dockview.toJSON()))).toEqual({
|
||||||
|
activeGroup: 'group-1',
|
||||||
|
grid: {
|
||||||
|
root: {
|
||||||
|
type: 'branch',
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
type: 'leaf',
|
||||||
|
data: {
|
||||||
|
views: ['panel1'],
|
||||||
|
id: 'group-1',
|
||||||
|
activeView: 'panel1',
|
||||||
|
},
|
||||||
|
size: 500,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'leaf',
|
||||||
|
data: {
|
||||||
|
views: ['panel2'],
|
||||||
|
id: 'group-2',
|
||||||
|
activeView: 'panel2',
|
||||||
|
},
|
||||||
|
size: 500,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
size: 1000,
|
||||||
|
},
|
||||||
|
height: 1000,
|
||||||
|
width: 1000,
|
||||||
|
orientation: Orientation.VERTICAL,
|
||||||
|
},
|
||||||
|
panels: {
|
||||||
|
panel1: {
|
||||||
|
id: 'panel1',
|
||||||
|
view: { content: { id: 'default' } },
|
||||||
|
title: 'panel1',
|
||||||
|
},
|
||||||
|
panel2: {
|
||||||
|
id: 'panel2',
|
||||||
|
view: {
|
||||||
|
content: { id: 'default' },
|
||||||
|
tab: { id: 'test_tab_id' },
|
||||||
|
},
|
||||||
|
title: 'panel2',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
options: { tabHeight: 25 },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('#2', () => {
|
||||||
|
const container = document.createElement('div');
|
||||||
|
|
||||||
|
const dockview = new DockviewComponent(container, {
|
||||||
|
components: {
|
||||||
|
default: PanelContentPartTest,
|
||||||
|
},
|
||||||
|
tabComponents: {
|
||||||
|
test_tab_id: PanelTabPartTest,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
dockview.deserializer = new ReactPanelDeserialzier(dockview);
|
||||||
|
dockview.fromJSON({
|
||||||
|
activeGroup: 'group-1',
|
||||||
|
grid: {
|
||||||
|
root: {
|
||||||
|
type: 'branch',
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
type: 'leaf',
|
||||||
|
data: {
|
||||||
|
views: ['panel1'],
|
||||||
|
id: 'group-1',
|
||||||
|
activeView: 'panel1',
|
||||||
|
},
|
||||||
|
size: 500,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'leaf',
|
||||||
|
data: {
|
||||||
|
views: ['panel2', 'panel3'],
|
||||||
|
id: 'group-2',
|
||||||
|
activeView: 'panel2',
|
||||||
|
},
|
||||||
|
|
||||||
|
size: 500,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
size: 1000,
|
||||||
|
},
|
||||||
|
height: 1000,
|
||||||
|
width: 1000,
|
||||||
|
orientation: Orientation.VERTICAL,
|
||||||
|
},
|
||||||
|
panels: {
|
||||||
|
panel1: {
|
||||||
|
id: 'panel1',
|
||||||
|
view: { content: { id: 'default' } },
|
||||||
|
title: 'panel1',
|
||||||
|
},
|
||||||
|
panel2: {
|
||||||
|
id: 'panel2',
|
||||||
|
view: {
|
||||||
|
content: { id: 'default' },
|
||||||
|
tab: { id: 'test_tab_id' },
|
||||||
|
},
|
||||||
|
title: 'panel2',
|
||||||
|
},
|
||||||
|
panel3: {
|
||||||
|
id: 'panel3',
|
||||||
|
view: { content: { id: 'default' } },
|
||||||
|
title: 'panel3',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
options: { tabHeight: 25 },
|
||||||
|
});
|
||||||
|
|
||||||
|
const group = dockview.getGroupPanel('panel2')!.api.group;
|
||||||
|
|
||||||
|
const viewQuery = group.element.querySelectorAll(
|
||||||
|
'.groupview > .tabs-and-actions-container > .tabs-container > .tab'
|
||||||
|
);
|
||||||
|
expect(viewQuery.length).toBe(2);
|
||||||
|
|
||||||
|
const viewQuery2 = group.element.querySelectorAll(
|
||||||
|
'.groupview > .tabs-and-actions-container > .tabs-container > .tab > .default-tab'
|
||||||
|
);
|
||||||
|
expect(viewQuery2.length).toBe(1);
|
||||||
|
|
||||||
|
const viewQuery3 = group.element.querySelectorAll(
|
||||||
|
'.groupview > .tabs-and-actions-container > .tabs-container > .tab > .panel-tab-part-test_tab_id'
|
||||||
|
);
|
||||||
|
expect(viewQuery3.length).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
// load a layout with a default tab identifier when react default is present
|
||||||
|
|
||||||
|
// load a layout with invialid panel identifier
|
||||||
});
|
});
|
||||||
|
@ -11,20 +11,19 @@
|
|||||||
margin: 0px;
|
margin: 0px;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
|
|
||||||
a:active {
|
|
||||||
-webkit-mask-size: 100% 100% !important;
|
|
||||||
mask-size: 100% 100% !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.close-action {
|
.close-action {
|
||||||
background-color: white;
|
padding: 4px;
|
||||||
height: 16px;
|
display: flex;
|
||||||
width: 16px;
|
align-items: center;
|
||||||
display: block;
|
justify-content: center;
|
||||||
-webkit-mask: var(--dv-tab-close-icon) 50% 50% / 90% 90% no-repeat;
|
box-sizing: border-box;
|
||||||
mask: var(--dv-tab-close-icon) 50% 50% / 90% 90% no-repeat;
|
|
||||||
margin-right: '0.5em';
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
color: var(--dv-activegroup-hiddenpanel-tab-color);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-radius: 2px;
|
||||||
|
background-color: var(--dv-icon-hover-background-color);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,17 +38,17 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
min-width: 80px;
|
min-width: 80px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding-left: 10px;
|
padding: 0px 8px;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
text-overflow: elipsis;
|
text-overflow: elipsis;
|
||||||
|
|
||||||
.tab-content {
|
.tab-content {
|
||||||
|
padding: 0px 8px;
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.action-container {
|
.action-container {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
width: 28px;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
||||||
.tab-list {
|
.tab-list {
|
||||||
@ -57,19 +57,17 @@
|
|||||||
margin: 0px;
|
margin: 0px;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
|
|
||||||
a:active:hover {
|
|
||||||
-webkit-mask-size: 100% 100% !important;
|
|
||||||
mask-size: 100% 100% !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tab-action {
|
.tab-action {
|
||||||
height: 16px;
|
padding: 4px;
|
||||||
width: 16px;
|
display: flex;
|
||||||
display: block;
|
align-items: center;
|
||||||
-webkit-mask: var(--dv-tab-close-icon) 50% 50% / 90% 90%
|
justify-content: center;
|
||||||
no-repeat;
|
box-sizing: border-box;
|
||||||
mask: var(--dv-tab-close-icon) 50% 50% / 90% 90% no-repeat;
|
|
||||||
margin-right: '0.5em';
|
&:hover {
|
||||||
|
border-radius: 2px;
|
||||||
|
background-color: var(--dv-icon-hover-background-color);
|
||||||
|
}
|
||||||
|
|
||||||
&.disable-close {
|
&.disable-close {
|
||||||
display: none;
|
display: none;
|
||||||
|
@ -6,6 +6,8 @@ import {
|
|||||||
import { addDisposableListener } from '../../../events';
|
import { addDisposableListener } from '../../../events';
|
||||||
import { PanelUpdateEvent } from '../../../panel/types';
|
import { PanelUpdateEvent } from '../../../panel/types';
|
||||||
import { GroupPanel } from '../../../groupview/groupviewPanel';
|
import { GroupPanel } from '../../../groupview/groupviewPanel';
|
||||||
|
import { createCloseButton } from '../../../svg';
|
||||||
|
import { DEFAULT_TAB_IDENTIFIER } from '../../../react';
|
||||||
|
|
||||||
export class DefaultTab extends CompositeDisposable implements ITabRenderer {
|
export class DefaultTab extends CompositeDisposable implements ITabRenderer {
|
||||||
private _element: HTMLElement;
|
private _element: HTMLElement;
|
||||||
@ -24,7 +26,7 @@ export class DefaultTab extends CompositeDisposable implements ITabRenderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get id() {
|
get id() {
|
||||||
return '__DEFAULT_TAB__';
|
return DEFAULT_TAB_IDENTIFIER;
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
@ -42,8 +44,10 @@ export class DefaultTab extends CompositeDisposable implements ITabRenderer {
|
|||||||
this._list = document.createElement('ul');
|
this._list = document.createElement('ul');
|
||||||
this._list.className = 'tab-list';
|
this._list.className = 'tab-list';
|
||||||
//
|
//
|
||||||
this.action = document.createElement('a');
|
this.action = document.createElement('div');
|
||||||
this.action.className = 'tab-action';
|
this.action.className = 'tab-action';
|
||||||
|
this.action.appendChild(createCloseButton());
|
||||||
|
|
||||||
//
|
//
|
||||||
this._element.appendChild(this._content);
|
this._element.appendChild(this._content);
|
||||||
this._element.appendChild(this._actionContainer);
|
this._element.appendChild(this._actionContainer);
|
||||||
|
@ -8,6 +8,7 @@ import { toggleClass } from '../../../dom';
|
|||||||
import { CompositeDisposable } from '../../../lifecycle';
|
import { CompositeDisposable } from '../../../lifecycle';
|
||||||
import { GroupPanel } from '../../../groupview/groupviewPanel';
|
import { GroupPanel } from '../../../groupview/groupviewPanel';
|
||||||
import { PanelUpdateEvent } from '../../../panel/types';
|
import { PanelUpdateEvent } from '../../../panel/types';
|
||||||
|
import { createCloseButton } from '../../../svg';
|
||||||
|
|
||||||
export class Watermark
|
export class Watermark
|
||||||
extends CompositeDisposable
|
extends CompositeDisposable
|
||||||
@ -42,8 +43,9 @@ export class Watermark
|
|||||||
title.appendChild(emptySpace);
|
title.appendChild(emptySpace);
|
||||||
title.appendChild(actions.element);
|
title.appendChild(actions.element);
|
||||||
|
|
||||||
const closeAnchor = document.createElement('a');
|
const closeAnchor = document.createElement('div');
|
||||||
closeAnchor.className = 'close-action';
|
closeAnchor.className = 'close-action';
|
||||||
|
closeAnchor.appendChild(createCloseButton());
|
||||||
|
|
||||||
actions.add(closeAnchor);
|
actions.add(closeAnchor);
|
||||||
|
|
||||||
|
@ -18,24 +18,12 @@
|
|||||||
--dv-activegroup-visiblepanel-tab-background-color
|
--dv-activegroup-visiblepanel-tab-background-color
|
||||||
);
|
);
|
||||||
color: var(--dv-activegroup-visiblepanel-tab-color);
|
color: var(--dv-activegroup-visiblepanel-tab-color);
|
||||||
|
|
||||||
.tab-action {
|
|
||||||
background-color: var(
|
|
||||||
--dv-activegroup-visiblepanel-tab-color
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
&.inactive-tab {
|
&.inactive-tab {
|
||||||
background-color: var(
|
background-color: var(
|
||||||
--dv-activegroup-hiddenpanel-tab-background-color
|
--dv-activegroup-hiddenpanel-tab-background-color
|
||||||
);
|
);
|
||||||
color: var(--dv-activegroup-hiddenpanel-tab-color);
|
color: var(--dv-activegroup-hiddenpanel-tab-color);
|
||||||
|
|
||||||
.tab-action {
|
|
||||||
background-color: var(
|
|
||||||
--dv-activegroup-hiddenpanel-tab-color
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -46,24 +34,12 @@
|
|||||||
--dv-inactivegroup-visiblepanel-tab-background-color
|
--dv-inactivegroup-visiblepanel-tab-background-color
|
||||||
);
|
);
|
||||||
color: var(--dv-inactivegroup-visiblepanel-tab-color);
|
color: var(--dv-inactivegroup-visiblepanel-tab-color);
|
||||||
|
|
||||||
.tab-action {
|
|
||||||
background-color: var(
|
|
||||||
--dv-inactivegroup-visiblepanel-tab-color
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
&.inactive-tab {
|
&.inactive-tab {
|
||||||
background-color: var(
|
background-color: var(
|
||||||
--dv-inactivegroup-hiddenpanel-tab-background-color
|
--dv-inactivegroup-hiddenpanel-tab-background-color
|
||||||
);
|
);
|
||||||
color: var(--dv-inactivegroup-hiddenpanel-tab-color);
|
color: var(--dv-inactivegroup-hiddenpanel-tab-color);
|
||||||
|
|
||||||
.tab-action {
|
|
||||||
background-color: var(
|
|
||||||
--dv-inactivegroup-hiddenpanel-tab-color
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -72,6 +72,7 @@ export type DockviewComponentUpdateOptions = Pick<
|
|||||||
| 'frameworkTabComponents'
|
| 'frameworkTabComponents'
|
||||||
| 'showDndOverlay'
|
| 'showDndOverlay'
|
||||||
| 'watermarkFrameworkComponent'
|
| 'watermarkFrameworkComponent'
|
||||||
|
| 'defaultTabComponent'
|
||||||
>;
|
>;
|
||||||
|
|
||||||
export interface DockviewDropEvent extends GroupviewDropEvent {
|
export interface DockviewDropEvent extends GroupviewDropEvent {
|
||||||
@ -751,7 +752,7 @@ export class DockviewComponent
|
|||||||
private createPanel(options: AddPanelOptions, group: GroupPanel): IDockviewPanel {
|
private createPanel(options: AddPanelOptions, group: GroupPanel): IDockviewPanel {
|
||||||
const view = new DefaultGroupPanelView({
|
const view = new DefaultGroupPanelView({
|
||||||
content: this.createContentComponent(options.id, options.component),
|
content: this.createContentComponent(options.id, options.component),
|
||||||
tab: this.createTabComponent(options.id, options.tabComponent),
|
tab: this.createTabComponent(options.id, options.tabComponent || this.options.defaultTabComponent),
|
||||||
});
|
});
|
||||||
|
|
||||||
const panel = new DockviewGroupPanel(options.id, this, this._api, group);
|
const panel = new DockviewGroupPanel(options.id, this, this._api, group);
|
||||||
|
@ -62,6 +62,7 @@ export interface DockviewComponentOptions extends DockviewRenderFunctions {
|
|||||||
tabHeight?: number;
|
tabHeight?: number;
|
||||||
orientation?: Orientation;
|
orientation?: Orientation;
|
||||||
styles?: ISplitviewStyles;
|
styles?: ISplitviewStyles;
|
||||||
|
defaultTabComponent?: string;
|
||||||
showDndOverlay?: (event: DockviewDndOverlayEvent) => boolean;
|
showDndOverlay?: (event: DockviewDndOverlayEvent) => boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,11 +4,14 @@ import { CompositeDisposable, MutableDisposable } from '../lifecycle';
|
|||||||
import { PanelUpdateEvent } from '../panel/types';
|
import { PanelUpdateEvent } from '../panel/types';
|
||||||
import { IPaneHeaderPart, PanePanelInitParameter } from './paneviewPanel';
|
import { IPaneHeaderPart, PanePanelInitParameter } from './paneviewPanel';
|
||||||
import { toggleClass } from '../dom';
|
import { toggleClass } from '../dom';
|
||||||
|
import { createChevronRightButton, createExpandMoreButton } from '../svg';
|
||||||
|
|
||||||
export class DefaultHeader
|
export class DefaultHeader
|
||||||
extends CompositeDisposable
|
extends CompositeDisposable
|
||||||
implements IPaneHeaderPart
|
implements IPaneHeaderPart
|
||||||
{
|
{
|
||||||
|
private readonly _expandedIcon = createExpandMoreButton();
|
||||||
|
private readonly _collapsedIcon = createChevronRightButton();
|
||||||
private readonly disposable = new MutableDisposable();
|
private readonly disposable = new MutableDisposable();
|
||||||
private readonly _element: HTMLElement;
|
private readonly _element: HTMLElement;
|
||||||
private readonly _content: HTMLElement;
|
private readonly _content: HTMLElement;
|
||||||
@ -21,11 +24,13 @@ export class DefaultHeader
|
|||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this._element = document.createElement('div');
|
this._element = document.createElement('div');
|
||||||
this.element.className = 'default-header';
|
this.element.className = 'default-header';
|
||||||
|
|
||||||
this._content = document.createElement('span');
|
this._content = document.createElement('span');
|
||||||
this._expander = document.createElement('a');
|
this._expander = document.createElement('div');
|
||||||
|
this._expander.className = 'dockview-pane-header-icon';
|
||||||
|
|
||||||
this.element.appendChild(this._expander);
|
this.element.appendChild(this._expander);
|
||||||
this.element.appendChild(this._content);
|
this.element.appendChild(this._content);
|
||||||
@ -41,15 +46,35 @@ export class DefaultHeader
|
|||||||
this.apiRef.api = params.api;
|
this.apiRef.api = params.api;
|
||||||
|
|
||||||
this._content.textContent = params.title;
|
this._content.textContent = params.title;
|
||||||
this._expander.textContent = '▼';
|
|
||||||
|
|
||||||
toggleClass(this._expander, 'collapsed', !params.api.isExpanded);
|
this.updateIcon();
|
||||||
|
|
||||||
this.disposable.value = params.api.onDidExpansionChange((e) => {
|
this.disposable.value = params.api.onDidExpansionChange(() => {
|
||||||
toggleClass(this._expander, 'collapsed', !e.isExpanded);
|
this.updateIcon();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private updateIcon() {
|
||||||
|
const isExpanded = !!this.apiRef.api?.isExpanded;
|
||||||
|
toggleClass(this._expander, 'collapsed', !isExpanded);
|
||||||
|
|
||||||
|
if (isExpanded) {
|
||||||
|
if (this._expander.contains(this._collapsedIcon)) {
|
||||||
|
this._collapsedIcon.remove();
|
||||||
|
}
|
||||||
|
if (!this._expander.contains(this._expandedIcon)) {
|
||||||
|
this._expander.appendChild(this._expandedIcon);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (this._expander.contains(this._expandedIcon)) {
|
||||||
|
this._expandedIcon.remove();
|
||||||
|
}
|
||||||
|
if (!this._expander.contains(this._collapsedIcon)) {
|
||||||
|
this._expander.appendChild(this._collapsedIcon);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
update(_params: PanelUpdateEvent) {
|
update(_params: PanelUpdateEvent) {
|
||||||
//
|
//
|
||||||
}
|
}
|
||||||
|
@ -31,8 +31,10 @@
|
|||||||
padding: 0px 8px;
|
padding: 0px 8px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
.collapsed {
|
.dockview-pane-header-icon {
|
||||||
transform: rotate(-90deg);
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
> span {
|
> span {
|
||||||
|
@ -7,6 +7,7 @@ import { DockviewApi } from '../api/component.api';
|
|||||||
import { DefaultTab } from '../dockview/components/tab/defaultTab';
|
import { DefaultTab } from '../dockview/components/tab/defaultTab';
|
||||||
import { DefaultGroupPanelView } from '../dockview/defaultGroupPanelView';
|
import { DefaultGroupPanelView } from '../dockview/defaultGroupPanelView';
|
||||||
import { GroupPanel } from '../groupview/groupviewPanel';
|
import { GroupPanel } from '../groupview/groupviewPanel';
|
||||||
|
import { ITabRenderer } from '../groupview/types';
|
||||||
|
|
||||||
export class ReactPanelDeserialzier implements IPanelDeserializer {
|
export class ReactPanelDeserialzier implements IPanelDeserializer {
|
||||||
constructor(private readonly layout: DockviewComponent) {}
|
constructor(private readonly layout: DockviewComponent) {}
|
||||||
@ -21,6 +22,30 @@ export class ReactPanelDeserialzier implements IPanelDeserializer {
|
|||||||
const suppressClosable = panelData.suppressClosable;
|
const suppressClosable = panelData.suppressClosable;
|
||||||
const viewData = panelData.view;
|
const viewData = panelData.view;
|
||||||
|
|
||||||
|
let tab: ITabRenderer;
|
||||||
|
|
||||||
|
if (viewData.tab?.id) {
|
||||||
|
tab = createComponent(
|
||||||
|
viewData.tab.id,
|
||||||
|
viewData.tab.id,
|
||||||
|
this.layout.options.tabComponents,
|
||||||
|
this.layout.options.frameworkTabComponents,
|
||||||
|
this.layout.options.frameworkComponentFactory?.tab,
|
||||||
|
() => new DefaultTab()
|
||||||
|
);
|
||||||
|
} else if (this.layout.options.defaultTabComponent) {
|
||||||
|
tab = createComponent(
|
||||||
|
this.layout.options.defaultTabComponent,
|
||||||
|
this.layout.options.defaultTabComponent,
|
||||||
|
this.layout.options.tabComponents,
|
||||||
|
this.layout.options.frameworkTabComponents,
|
||||||
|
this.layout.options.frameworkComponentFactory?.tab,
|
||||||
|
() => new DefaultTab()
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
tab = new DefaultTab();
|
||||||
|
}
|
||||||
|
|
||||||
const view = new DefaultGroupPanelView({
|
const view = new DefaultGroupPanelView({
|
||||||
content: createComponent(
|
content: createComponent(
|
||||||
viewData.content.id,
|
viewData.content.id,
|
||||||
@ -29,15 +54,7 @@ export class ReactPanelDeserialzier implements IPanelDeserializer {
|
|||||||
this.layout.options.frameworkComponents,
|
this.layout.options.frameworkComponents,
|
||||||
this.layout.options.frameworkComponentFactory?.content
|
this.layout.options.frameworkComponentFactory?.content
|
||||||
),
|
),
|
||||||
tab: viewData.tab?.id
|
tab,
|
||||||
? createComponent(
|
|
||||||
viewData.tab.id,
|
|
||||||
viewData.tab.id,
|
|
||||||
this.layout.options.tabComponents,
|
|
||||||
this.layout.options.frameworkTabComponents,
|
|
||||||
this.layout.options.frameworkComponentFactory?.tab
|
|
||||||
)
|
|
||||||
: new DefaultTab(),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const panel = new DockviewGroupPanel(
|
const panel = new DockviewGroupPanel(
|
||||||
|
32
packages/dockview/src/react/dockview/defaultTab.scss
Normal file
32
packages/dockview/src/react/dockview/defaultTab.scss
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
.tab {
|
||||||
|
.dockview-react-tab {
|
||||||
|
display: flex;
|
||||||
|
padding: 0px 8px;
|
||||||
|
align-items: center;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
.dockview-react-tab-title {
|
||||||
|
padding: 0px 8px;
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dockview-react-tab-action {
|
||||||
|
padding: 4px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-radius: 2px;
|
||||||
|
background-color: var(--dv-icon-hover-background-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.inactive-tab:not(:hover) {
|
||||||
|
.dockview-react-tab-action {
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
42
packages/dockview/src/react/dockview/defaultTab.tsx
Normal file
42
packages/dockview/src/react/dockview/defaultTab.tsx
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
import { IDockviewPanelHeaderProps } from './dockview';
|
||||||
|
import * as React from 'react';
|
||||||
|
import { CloseButton } from '../svg';
|
||||||
|
|
||||||
|
export type IDockviewDefaultTabProps = IDockviewPanelHeaderProps &
|
||||||
|
React.DOMAttributes<HTMLDivElement>;
|
||||||
|
|
||||||
|
export const DockviewDefaultTab: React.FunctionComponent<IDockviewDefaultTabProps> =
|
||||||
|
({ api, containerApi: _containerApi, params: _params, ...rest }) => {
|
||||||
|
const onClose = React.useCallback(
|
||||||
|
(event: React.MouseEvent<HTMLSpanElement>) => {
|
||||||
|
event.stopPropagation();
|
||||||
|
api.close();
|
||||||
|
},
|
||||||
|
[api]
|
||||||
|
);
|
||||||
|
|
||||||
|
const onClick = React.useCallback(
|
||||||
|
(event: React.MouseEvent<HTMLDivElement>) => {
|
||||||
|
api.setActive();
|
||||||
|
|
||||||
|
if (rest.onClick) {
|
||||||
|
rest.onClick(event);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[api, rest.onClick]
|
||||||
|
);
|
||||||
|
|
||||||
|
const iconClassname = React.useMemo(() => {
|
||||||
|
const cn = ['dockview-react-tab-action'];
|
||||||
|
return cn.join(',');
|
||||||
|
}, [api.suppressClosable]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div {...rest} onClick={onClick} className="dockview-react-tab">
|
||||||
|
<span className="dockview-react-tab-title">{api.title}</span>
|
||||||
|
<div className={iconClassname} onClick={onClose}>
|
||||||
|
<CloseButton />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
@ -19,6 +19,8 @@ import { PanelCollection, PanelParameters } from '../types';
|
|||||||
import { watchElementResize } from '../../dom';
|
import { watchElementResize } from '../../dom';
|
||||||
import { IContentRenderer, ITabRenderer } from '../../groupview/types';
|
import { IContentRenderer, ITabRenderer } from '../../groupview/types';
|
||||||
|
|
||||||
|
export const DEFAULT_TAB_IDENTIFIER = '__default__tab__';
|
||||||
|
|
||||||
export interface IGroupPanelBaseProps<T extends {} = Record<string, any>>
|
export interface IGroupPanelBaseProps<T extends {} = Record<string, any>>
|
||||||
extends PanelParameters<T> {
|
extends PanelParameters<T> {
|
||||||
api: DockviewPanelApi;
|
api: DockviewPanelApi;
|
||||||
@ -47,6 +49,7 @@ export interface IDockviewReactProps {
|
|||||||
hideBorders?: boolean;
|
hideBorders?: boolean;
|
||||||
className?: string;
|
className?: string;
|
||||||
disableAutoResizing?: boolean;
|
disableAutoResizing?: boolean;
|
||||||
|
defaultTabComponent?: React.FunctionComponent<IDockviewPanelHeaderProps>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DockviewReact = React.forwardRef(
|
export const DockviewReact = React.forwardRef(
|
||||||
@ -124,9 +127,13 @@ export const DockviewReact = React.forwardRef(
|
|||||||
const dockview = new DockviewComponent(element, {
|
const dockview = new DockviewComponent(element, {
|
||||||
frameworkComponentFactory: factory,
|
frameworkComponentFactory: factory,
|
||||||
frameworkComponents: props.components,
|
frameworkComponents: props.components,
|
||||||
frameworkTabComponents: props.tabComponents,
|
frameworkTabComponents: {
|
||||||
|
...(props.tabComponents || {}),
|
||||||
|
[DEFAULT_TAB_IDENTIFIER]: props.defaultTabComponent,
|
||||||
|
},
|
||||||
tabHeight: props.tabHeight,
|
tabHeight: props.tabHeight,
|
||||||
watermarkFrameworkComponent: props.watermarkComponent,
|
watermarkFrameworkComponent: props.watermarkComponent,
|
||||||
|
defaultTabComponent: DEFAULT_TAB_IDENTIFIER,
|
||||||
styles: props.hideBorders
|
styles: props.hideBorders
|
||||||
? { separatorBorder: 'transparent' }
|
? { separatorBorder: 'transparent' }
|
||||||
: undefined,
|
: undefined,
|
||||||
@ -222,6 +229,19 @@ export const DockviewReact = React.forwardRef(
|
|||||||
};
|
};
|
||||||
}, [props.onTabContextMenu]);
|
}, [props.onTabContextMenu]);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (!dockviewRef.current) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
dockviewRef.current.updateOptions({
|
||||||
|
defaultTabComponent: DEFAULT_TAB_IDENTIFIER,
|
||||||
|
frameworkTabComponents: {
|
||||||
|
...(props.tabComponents || {}),
|
||||||
|
[DEFAULT_TAB_IDENTIFIER]: props.defaultTabComponent,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}, [props.defaultTabComponent]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={props.className}
|
className={props.className}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
export * from './dockview/dockview';
|
export * from './dockview/dockview';
|
||||||
|
export * from './dockview/defaultTab';
|
||||||
export * from './splitview/splitview';
|
export * from './splitview/splitview';
|
||||||
export * from './gridview/gridview';
|
export * from './gridview/gridview';
|
||||||
export * from './dockview/reactContentPart';
|
export * from './dockview/reactContentPart';
|
||||||
|
29
packages/dockview/src/react/svg.tsx
Normal file
29
packages/dockview/src/react/svg.tsx
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
|
||||||
|
export const CloseButton = () => (
|
||||||
|
<svg
|
||||||
|
height="11"
|
||||||
|
width="11"
|
||||||
|
viewBox="0 0 28 28"
|
||||||
|
aria-hidden={'false'}
|
||||||
|
focusable={false}
|
||||||
|
className="dockview-svg"
|
||||||
|
>
|
||||||
|
<path d="M2.1 27.3L0 25.2L11.55 13.65L0 2.1L2.1 0L13.65 11.55L25.2 0L27.3 2.1L15.75 13.65L27.3 25.2L25.2 27.3L13.65 15.75L2.1 27.3Z"></path>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
|
||||||
|
export const ExpandMore = () => {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
width="11"
|
||||||
|
height="11"
|
||||||
|
viewBox="0 0 24 15"
|
||||||
|
aria-hidden={'false'}
|
||||||
|
focusable={false}
|
||||||
|
className="dockview-svg"
|
||||||
|
>
|
||||||
|
<path d="M12 14.15L0 2.15L2.15 0L12 9.9L21.85 0.0499992L24 2.2L12 14.15Z" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
};
|
7
packages/dockview/src/svg.scss
Normal file
7
packages/dockview/src/svg.scss
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
.dockview-svg {
|
||||||
|
display: inline-block;
|
||||||
|
fill: currentcolor;
|
||||||
|
line-height: 1;
|
||||||
|
stroke: currentcolor;
|
||||||
|
stroke-width: 0;
|
||||||
|
}
|
42
packages/dockview/src/svg.ts
Normal file
42
packages/dockview/src/svg.ts
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
const createSvgElementFromPath = (params: {
|
||||||
|
height: string;
|
||||||
|
width: string;
|
||||||
|
viewbox: string;
|
||||||
|
path: string;
|
||||||
|
}) => {
|
||||||
|
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
||||||
|
svg.setAttributeNS(null, 'height', params.height);
|
||||||
|
svg.setAttributeNS(null, 'width', params.width);
|
||||||
|
svg.setAttributeNS(null, 'viewBox', params.viewbox);
|
||||||
|
svg.setAttributeNS(null, 'aria-hidden', 'false');
|
||||||
|
svg.setAttributeNS(null, 'focusable', 'false');
|
||||||
|
svg.classList.add('dockview-svg');
|
||||||
|
const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
|
||||||
|
path.setAttributeNS(null, 'd', params.path);
|
||||||
|
svg.appendChild(path);
|
||||||
|
return svg;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createCloseButton = () =>
|
||||||
|
createSvgElementFromPath({
|
||||||
|
width: '11',
|
||||||
|
height: '11',
|
||||||
|
viewbox: '0 0 28 28',
|
||||||
|
path: 'M2.1 27.3L0 25.2L11.55 13.65L0 2.1L2.1 0L13.65 11.55L25.2 0L27.3 2.1L15.75 13.65L27.3 25.2L25.2 27.3L13.65 15.75L2.1 27.3Z',
|
||||||
|
});
|
||||||
|
|
||||||
|
export const createExpandMoreButton = () =>
|
||||||
|
createSvgElementFromPath({
|
||||||
|
width: '11',
|
||||||
|
height: '11',
|
||||||
|
viewbox: '0 0 24 15',
|
||||||
|
path: 'M12 14.15L0 2.15L2.15 0L12 9.9L21.85 0.0499992L24 2.2L12 14.15Z',
|
||||||
|
});
|
||||||
|
|
||||||
|
export const createChevronRightButton = () =>
|
||||||
|
createSvgElementFromPath({
|
||||||
|
width: '11',
|
||||||
|
height: '11',
|
||||||
|
viewbox: '0 0 15 25',
|
||||||
|
path: 'M2.15 24.1L0 21.95L9.9 12.05L0 2.15L2.15 0L14.2 12.05L2.15 24.1Z',
|
||||||
|
});
|
@ -2,10 +2,10 @@
|
|||||||
--dv-paneview-active-outline-color: dodgerblue;
|
--dv-paneview-active-outline-color: dodgerblue;
|
||||||
--dv-tabs-and-actions-container-font-size: 13px;
|
--dv-tabs-and-actions-container-font-size: 13px;
|
||||||
--dv-tabs-and-actions-container-height: 35px;
|
--dv-tabs-and-actions-container-height: 35px;
|
||||||
--dv-tab-close-icon: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M0 0h24v24H0z" fill="none"/><path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg>');
|
|
||||||
--dv-drag-over-background-color: rgba(83, 89, 93, 0.5);
|
--dv-drag-over-background-color: rgba(83, 89, 93, 0.5);
|
||||||
--dv-drag-over-border-color: white;
|
--dv-drag-over-border-color: white;
|
||||||
--dv-tabs-container-scrollbar-color: #888;
|
--dv-tabs-container-scrollbar-color: #888;
|
||||||
|
--dv-icon-hover-background-color: rgba(90, 93, 94, 0.31);
|
||||||
}
|
}
|
||||||
|
|
||||||
@mixin dockview-theme-dark-mixin {
|
@mixin dockview-theme-dark-mixin {
|
||||||
|
Loading…
Reference in New Issue
Block a user