Compare commits

..

No commits in common. "master" and "v4.8.0" have entirely different histories.

59 changed files with 1037 additions and 3523 deletions

View File

@ -2,7 +2,7 @@
"packages": [
"packages/*"
],
"version": "4.11.0",
"version": "4.8.0",
"npmClient": "yarn",
"command": {
"publish": {

View File

@ -1,6 +1,6 @@
{
"name": "dockview-angular",
"version": "4.11.0",
"version": "4.8.0",
"description": "Zero dependency layout manager supporting tabs, grids and splitviews",
"keywords": [
"splitview",
@ -53,7 +53,7 @@
"test:cov": "cross-env ../../node_modules/.bin/jest --selectProjects dockview-angular --coverage"
},
"dependencies": {
"dockview-core": "^4.11.0"
"dockview-core": "^4.7.1"
},
"peerDependencies": {
"@angular/common": ">=14.0.0",

View File

@ -1,6 +1,6 @@
{
"name": "dockview-core",
"version": "4.11.0",
"version": "4.7.1",
"description": "Zero dependency layout manager supporting tabs, grids and splitviews",
"keywords": [
"splitview",

View File

@ -132,12 +132,12 @@ describe('dockviewGroupPanel', () => {
cut.model.openPanel(panel);
// explicit group constraints now override panel constraints
// active panel constraints
expect(cut.minimumWidth).toBe(20); // group constraint overrides panel constraint
expect(cut.minimumHeight).toBe(10); // group constraint overrides panel constraint
expect(cut.maximumHeight).toBe(100); // group constraint overrides panel constraint
expect(cut.maximumWidth).toBe(200); // group constraint overrides panel constraint
expect(cut.minimumWidth).toBe(21);
expect(cut.minimumHeight).toBe(11);
expect(cut.maximumHeight).toBe(101);
expect(cut.maximumWidth).toBe(201);
const panel2 = new DockviewPanel(
'panel_id',
@ -158,12 +158,12 @@ describe('dockviewGroupPanel', () => {
cut.model.openPanel(panel2);
// explicit group constraints still override panel constraints
// active panel constraints
expect(cut.minimumWidth).toBe(20); // group constraint overrides panel constraint
expect(cut.minimumHeight).toBe(10); // group constraint overrides panel constraint
expect(cut.maximumHeight).toBe(100); // group constraint overrides panel constraint
expect(cut.maximumWidth).toBe(200); // group constraint overrides panel constraint
expect(cut.minimumWidth).toBe(22);
expect(cut.minimumHeight).toBe(12);
expect(cut.maximumHeight).toBe(102);
expect(cut.maximumWidth).toBe(202);
const panel3 = new DockviewPanel(
'panel_id',

View File

@ -2936,69 +2936,4 @@ describe('gridview', () => {
expect(panel1.api.isVisible).toBeTruthy();
expect(panel2.api.isVisible).toBeTruthy();
});
test('registerPanel is called after doAddGroup - panel api events work immediately', () => {
// This test verifies the fix for the timing issue where registerPanel
// was called before doAddGroup, causing "Cannot read properties of undefined" errors
const gridview = new GridviewComponent(container, {
proportionalLayout: false,
orientation: Orientation.VERTICAL,
createComponent: (options) => {
switch (options.name) {
case 'default':
return new TestGridview(options.id, options.name);
default:
throw new Error('unsupported');
}
},
});
gridview.layout(800, 400);
// Add first panel
const panel1 = gridview.addPanel({
id: 'panel_1',
component: 'default',
});
// Verify the panel API is immediately accessible and functional
expect(panel1.api).toBeDefined();
expect(panel1.api.onDidFocusChange).toBeDefined();
// Subscribe to focus events to verify event subscription works
let focusEventCount = 0;
const disposable = panel1.api.onDidFocusChange((event) => {
focusEventCount++;
});
// This should not throw an error - before the fix, this would throw:
// "Cannot read properties of undefined (reading 'onDidFocusChange')"
const panel2 = gridview.addPanel({
id: 'panel_2',
component: 'default',
position: { referencePanel: panel1.id, direction: 'right' },
});
// Verify both panels have working APIs
expect(panel1.api).toBeDefined();
expect(panel2.api).toBeDefined();
expect(panel1.api.onDidFocusChange).toBeDefined();
expect(panel2.api.onDidFocusChange).toBeDefined();
// Verify that the API is functional by checking properties
expect(panel1.api.isVisible).toBeTruthy();
expect(panel2.api.isVisible).toBeTruthy();
// Verify we can subscribe to events on the second panel too
const disposable2 = panel2.api.onDidFocusChange((event) => {
focusEventCount++;
});
// Clean up
disposable.dispose();
disposable2.dispose();
// The main test is that we got this far without errors
expect(true).toBeTruthy();
});
});

View File

@ -33,7 +33,7 @@ export interface DockviewPanelApi
extends Omit<
GridviewPanelApi,
// omit properties that do not make sense here
'setVisible' | 'onDidConstraintsChange'
'setVisible' | 'onDidConstraintsChange' | 'setConstraints'
> {
/**
* The id of the tab component renderer

View File

@ -12,7 +12,7 @@ export interface IPanelDeserializer {
): IDockviewPanel;
}
// @deprecated
// @depreciated
interface LegacyState extends GroupviewPanelState {
view?: {
tab?: { id: string };

View File

@ -7,13 +7,12 @@ import {
IHeader,
DockviewGroupPanelLocked,
} from './dockviewGroupPanelModel';
import { GridviewPanel, IGridviewPanel, Contraints } from '../gridview/gridviewPanel';
import { GridviewPanel, IGridviewPanel } from '../gridview/gridviewPanel';
import { IDockviewPanel } from '../dockview/dockviewPanel';
import {
DockviewGroupPanelApi,
DockviewGroupPanelApiImpl,
} from '../api/dockviewGroupPanelApi';
// GridConstraintChangeEvent2 is not exported, so we'll type it manually
const MINIMUM_DOCKVIEW_GROUP_PANEL_WIDTH = 100;
const MINIMUM_DOCKVIEW_GROUP_PANEL_HEIGHT = 100;
@ -34,16 +33,8 @@ export class DockviewGroupPanel
implements IDockviewGroupPanel
{
private readonly _model: DockviewGroupPanelModel;
// Track explicitly set constraints to override panel constraints
private _explicitConstraints: Partial<Contraints> = {};
override get minimumWidth(): number {
// Check for explicitly set group constraint first
if (typeof this._explicitConstraints.minimumWidth === 'number') {
return this._explicitConstraints.minimumWidth;
}
const activePanelMinimumWidth = this.activePanel?.minimumWidth;
if (typeof activePanelMinimumWidth === 'number') {
return activePanelMinimumWidth;
@ -52,11 +43,6 @@ export class DockviewGroupPanel
}
override get minimumHeight(): number {
// Check for explicitly set group constraint first
if (typeof this._explicitConstraints.minimumHeight === 'number') {
return this._explicitConstraints.minimumHeight;
}
const activePanelMinimumHeight = this.activePanel?.minimumHeight;
if (typeof activePanelMinimumHeight === 'number') {
return activePanelMinimumHeight;
@ -65,11 +51,6 @@ export class DockviewGroupPanel
}
override get maximumWidth(): number {
// Check for explicitly set group constraint first
if (typeof this._explicitConstraints.maximumWidth === 'number') {
return this._explicitConstraints.maximumWidth;
}
const activePanelMaximumWidth = this.activePanel?.maximumWidth;
if (typeof activePanelMaximumWidth === 'number') {
return activePanelMaximumWidth;
@ -78,11 +59,6 @@ export class DockviewGroupPanel
}
override get maximumHeight(): number {
// Check for explicitly set group constraint first
if (typeof this._explicitConstraints.maximumHeight === 'number') {
return this._explicitConstraints.maximumHeight;
}
const activePanelMaximumHeight = this.activePanel?.maximumHeight;
if (typeof activePanelMaximumHeight === 'number') {
return activePanelMaximumHeight;
@ -131,7 +107,7 @@ export class DockviewGroupPanel
options.constraints?.minimumHeight ??
MINIMUM_DOCKVIEW_GROUP_PANEL_HEIGHT,
minimumWidth:
options.constraints?.minimumWidth ??
options.constraints?.maximumHeight ??
MINIMUM_DOCKVIEW_GROUP_PANEL_WIDTH,
maximumHeight: options.constraints?.maximumHeight,
maximumWidth: options.constraints?.maximumWidth,
@ -152,30 +128,6 @@ export class DockviewGroupPanel
this.addDisposables(
this.model.onDidActivePanelChange((event) => {
this.api._onDidActivePanelChange.fire(event);
}),
this.api.onDidConstraintsChangeInternal((event: any) => {
// Track explicitly set constraints to override panel constraints
// Extract numeric values from functions or values
if (event.minimumWidth !== undefined) {
this._explicitConstraints.minimumWidth = typeof event.minimumWidth === 'function'
? event.minimumWidth()
: event.minimumWidth;
}
if (event.minimumHeight !== undefined) {
this._explicitConstraints.minimumHeight = typeof event.minimumHeight === 'function'
? event.minimumHeight()
: event.minimumHeight;
}
if (event.maximumWidth !== undefined) {
this._explicitConstraints.maximumWidth = typeof event.maximumWidth === 'function'
? event.maximumWidth()
: event.maximumWidth;
}
if (event.maximumHeight !== undefined) {
this._explicitConstraints.maximumHeight = typeof event.maximumHeight === 'function'
? event.maximumHeight()
: event.maximumHeight;
}
})
);
}

View File

@ -366,8 +366,9 @@ export class GridviewComponent
isVisible: true,
});
this.doAddGroup(view, relativeLocation, options.size);
this.registerPanel(view);
this.doAddGroup(view, relativeLocation, options.size);
this.doSetGroupActive(view);
return view;

View File

@ -1,6 +1,6 @@
{
"name": "dockview-react",
"version": "4.11.0",
"version": "4.7.1",
"description": "Zero dependency layout manager supporting tabs, grids and splitviews",
"keywords": [
"splitview",
@ -53,6 +53,6 @@
"test:cov": "cross-env ../../node_modules/.bin/jest --selectProjects dockview-react --coverage"
},
"dependencies": {
"dockview": "^4.11.0"
"dockview": "^4.7.1"
}
}

View File

@ -10,13 +10,13 @@ const config: JestConfigWithTsJest = {
'<rootDir>/packages/dockview-vue/src/**/*.{js,jsx,ts,tsx}',
],
setupFiles: [
'<rootDir>/packages/dockview-vue/src/__tests__/__mocks__/resizeObserver.js',
// '<rootDir>/packages/dockview-vue/src/__tests__/__mocks__/resizeObserver.js',
],
setupFilesAfterEnv: ['<rootDir>/jest-setup.ts'],
coveragePathIgnorePatterns: ['/node_modules/'],
modulePathIgnorePatterns: [
'<rootDir>/packages/dockview-vue/src/__tests__/__mocks__',
'<rootDir>/packages/dockview-vue/src/__tests__/__test_utils__',
// '<rootDir>/packages/dockview-vue/src/__tests__/__mocks__',
// '<rootDir>/packages/dockview-vue/src/__tests__/__test_utils__',
],
coverageDirectory: '<rootDir>/packages/dockview-vue/coverage/',
testResultsProcessor: 'jest-sonar-reporter',

View File

@ -1,6 +1,6 @@
{
"name": "dockview-vue",
"version": "4.11.0",
"version": "4.7.1",
"description": "Zero dependency layout manager supporting tabs, grids and splitviews",
"keywords": [
"splitview",
@ -53,13 +53,9 @@
"test:cov": "cross-env ../../node_modules/.bin/jest --selectProjects dockview-vue --coverage"
},
"dependencies": {
"dockview-core": "^4.11.0"
"dockview-core": "^4.7.1"
},
"peerDependencies": {
"vue": "^3.4.0"
},
"devDependencies": {
"@vue/test-utils": "^2.4.0-alpha.2",
"@vue/vue3-jest": "^29.2.6"
}
}

View File

@ -1,5 +0,0 @@
global.ResizeObserver = jest.fn().mockImplementation(() => ({
observe: jest.fn(),
unobserve: jest.fn(),
disconnect: jest.fn(),
}));

View File

@ -1,53 +0,0 @@
import { ComponentInternalInstance, getCurrentInstance } from 'vue';
export function setMockRefElement() {
const mockElement = document.createElement('div');
mockElement.style.width = '1000px';
mockElement.style.height = '800px';
Object.defineProperty(mockElement, 'clientWidth', {
configurable: true,
value: 1000,
});
Object.defineProperty(mockElement, 'clientHeight', {
configurable: true,
value: 800,
});
Object.defineProperty(mockElement, 'offsetWidth', {
configurable: true,
value: 1000,
});
Object.defineProperty(mockElement, 'offsetHeight', {
configurable: true,
value: 800,
});
return mockElement;
}
export function createMockVueInstance(): ComponentInternalInstance {
return {
appContext: {
app: {} as any,
config: {} as any,
mixins: [],
components: {
'test-component': {
props: { params: Object, api: Object, containerApi: Object },
template: '<div>Test Component</div>'
}
},
directives: {},
provides: {},
globalProperties: {},
},
parent: null,
components: {
'test-component': {
props: { params: Object, api: Object, containerApi: Object },
template: '<div>Test Component</div>'
}
},
provides: {},
} as any;
}

View File

@ -1,112 +0,0 @@
import { createDockview, PROPERTY_KEYS_DOCKVIEW } from 'dockview-core';
describe('DockviewVue Component', () => {
test('should export component types', () => {
const types = require('../dockview/types');
expect(types).toBeDefined();
expect(typeof types).toBe('object');
});
test('should export dockview-core functionality', () => {
expect(createDockview).toBeDefined();
expect(PROPERTY_KEYS_DOCKVIEW).toBeDefined();
expect(Array.isArray(PROPERTY_KEYS_DOCKVIEW)).toBe(true);
});
test('should have correct dockview properties', () => {
expect(PROPERTY_KEYS_DOCKVIEW).toContain('disableAutoResizing');
expect(PROPERTY_KEYS_DOCKVIEW).toContain('hideBorders');
expect(PROPERTY_KEYS_DOCKVIEW).toContain('theme');
expect(PROPERTY_KEYS_DOCKVIEW).toContain('singleTabMode');
});
test('should create dockview instance with DOM element', () => {
const element = document.createElement('div');
document.body.appendChild(element);
const mockRenderer = {
element: document.createElement('div'),
dispose: () => {},
update: () => {},
init: () => {}
};
const api = createDockview(element, {
disableAutoResizing: true,
hideBorders: false,
createComponent: () => mockRenderer
});
expect(api).toBeDefined();
expect(typeof api.layout).toBe('function');
expect(typeof api.dispose).toBe('function');
expect(typeof api.addPanel).toBe('function');
expect(typeof api.updateOptions).toBe('function');
api.dispose();
document.body.removeChild(element);
});
test('should handle framework component creation', () => {
const element = document.createElement('div');
document.body.appendChild(element);
let createdComponent: any;
const api = createDockview(element, {
createComponent: (options) => {
createdComponent = {
element: document.createElement('div'),
dispose: jest.fn(),
update: jest.fn(),
init: jest.fn()
};
return createdComponent;
}
});
// Add a panel to trigger component creation
api.addPanel({
id: 'test-panel',
component: 'test-component',
title: 'Test Panel'
});
expect(createdComponent).toBeDefined();
expect(createdComponent.element).toBeInstanceOf(HTMLElement);
api.dispose();
document.body.removeChild(element);
});
test('should handle option updates', () => {
const element = document.createElement('div');
document.body.appendChild(element);
const mockRenderer = {
element: document.createElement('div'),
dispose: () => {},
update: () => {},
init: () => {}
};
const api = createDockview(element, {
disableAutoResizing: false,
hideBorders: false,
createComponent: () => mockRenderer
});
// Update options
api.updateOptions({
disableAutoResizing: true,
hideBorders: true
});
// Test passes if no errors are thrown
expect(true).toBe(true);
api.dispose();
document.body.removeChild(element);
});
});

View File

@ -0,0 +1,5 @@
describe('empty', () => {
test('that passes', () => {
expect(true).toBeTruthy();
});
});

View File

@ -1,155 +0,0 @@
import { createGridview, PROPERTY_KEYS_GRIDVIEW, Orientation } from 'dockview-core';
import { VueGridviewPanelView } from '../gridview/view';
describe('GridviewVue Component', () => {
test('should export component types', () => {
const types = require('../gridview/types');
expect(types).toBeDefined();
expect(typeof types).toBe('object');
});
test('should export dockview-core functionality', () => {
const dockviewCore = require('dockview-core');
expect(dockviewCore.createGridview).toBeDefined();
expect(dockviewCore.PROPERTY_KEYS_GRIDVIEW).toBeDefined();
});
test('should have correct gridview properties', () => {
expect(PROPERTY_KEYS_GRIDVIEW).toContain('proportionalLayout');
expect(PROPERTY_KEYS_GRIDVIEW).toContain('hideBorders');
expect(PROPERTY_KEYS_GRIDVIEW).toContain('disableAutoResizing');
});
test('should create gridview instance with DOM element', () => {
const element = document.createElement('div');
document.body.appendChild(element);
const api = createGridview(element, {
orientation: Orientation.HORIZONTAL,
proportionalLayout: true,
hideBorders: false,
createComponent: () => new VueGridviewPanelView('test', 'test-component', { template: '<div>Test</div>' } as any, {} as any)
});
expect(api).toBeDefined();
expect(typeof api.layout).toBe('function');
expect(typeof api.dispose).toBe('function');
expect(typeof api.addPanel).toBe('function');
expect(typeof api.updateOptions).toBe('function');
api.dispose();
document.body.removeChild(element);
});
test('should handle proportional layout changes', () => {
const element = document.createElement('div');
document.body.appendChild(element);
const api = createGridview(element, {
orientation: Orientation.HORIZONTAL,
proportionalLayout: false,
createComponent: () => new VueGridviewPanelView('test', 'test-component', { template: '<div>Test</div>' } as any, {} as any)
});
// Update proportional layout
api.updateOptions({ proportionalLayout: true });
// Test passes if no errors are thrown
expect(true).toBe(true);
api.dispose();
document.body.removeChild(element);
});
test('should add and manage grid panels', () => {
const element = document.createElement('div');
document.body.appendChild(element);
const api = createGridview(element, {
orientation: Orientation.HORIZONTAL,
proportionalLayout: true,
createComponent: (options) => new VueGridviewPanelView(options.id, options.name, { template: '<div>Test</div>' } as any, {} as any)
});
// Add a panel
api.addPanel({
id: 'grid-panel-1',
component: 'test-component'
});
expect(api.panels.length).toBe(1);
expect(api.panels[0].id).toBe('grid-panel-1');
// Remove the panel
api.removePanel(api.panels[0]);
expect(api.panels.length).toBe(0);
api.dispose();
document.body.removeChild(element);
});
});
describe('VueGridviewPanelView', () => {
let mockVueInstance: any;
let mockVueComponent: any;
let panelView: VueGridviewPanelView;
beforeEach(() => {
mockVueInstance = {
appContext: { components: {} },
components: {},
parent: null,
};
mockVueComponent = {
props: { params: Object, api: Object, containerApi: Object },
template: '<div>Test Gridview Panel</div>',
};
panelView = new VueGridviewPanelView(
'gridview-test-id',
'gridview-test-component',
mockVueComponent as any,
mockVueInstance
);
});
test('should create panel view with correct properties', () => {
expect(panelView.id).toBe('gridview-test-id');
expect(panelView.element).toBeInstanceOf(HTMLElement);
expect(panelView.element.style.height).toBe('100%');
expect(panelView.element.style.width).toBe('100%');
expect(panelView.element.style.overflow).toBe('hidden');
});
test('should implement GridviewPanel interface', () => {
expect(panelView.api).toBeDefined();
expect(typeof panelView.getComponent).toBe('function');
expect(panelView.element).toBeInstanceOf(HTMLElement);
});
test('should create framework part when getComponent is called', () => {
// Mock _params to avoid accessor error
(panelView as any)._params = {
params: {},
accessor: { id: 'test-accessor' }
};
const component = panelView.getComponent();
expect(component).toBeDefined();
expect(component.constructor.name).toBe('VuePart');
});
test('should handle empty params', () => {
// Mock _params to avoid accessor error
(panelView as any)._params = {
params: {},
accessor: { id: 'test-accessor' }
};
const component = panelView.getComponent();
expect(component).toBeDefined();
});
});

View File

@ -1,126 +0,0 @@
import { createPaneview, PROPERTY_KEYS_PANEVIEW } from 'dockview-core';
import { VuePaneviewPanelView } from '../paneview/view';
describe('PaneviewVue Component', () => {
test('should export component types', () => {
const types = require('../paneview/types');
expect(types).toBeDefined();
expect(typeof types).toBe('object');
});
test('should export dockview-core functionality', () => {
const dockviewCore = require('dockview-core');
expect(dockviewCore.createPaneview).toBeDefined();
expect(dockviewCore.PROPERTY_KEYS_PANEVIEW).toBeDefined();
});
test('should have correct paneview properties', () => {
expect(PROPERTY_KEYS_PANEVIEW).toContain('disableAutoResizing');
expect(PROPERTY_KEYS_PANEVIEW).toContain('disableDnd');
});
test('should create paneview with Vue framework support', () => {
// Test that Vue-specific components can be created with proper type safety
expect(typeof createPaneview).toBe('function');
expect(typeof VuePaneviewPanelView).toBe('function');
// Test that a Vue paneview panel view can be instantiated
const mockVueComponent = { template: '<div>Test</div>' } as any;
const mockParent = {} as any;
const panelView = new VuePaneviewPanelView(
'test-id',
mockVueComponent,
mockParent
);
expect(panelView.id).toBe('test-id');
expect(panelView.element).toBeInstanceOf(HTMLElement);
expect(typeof panelView.init).toBe('function');
expect(typeof panelView.update).toBe('function');
expect(typeof panelView.dispose).toBe('function');
});
test('should handle Vue component integration for panes', () => {
// Test Vue component factory creation for paneview
const mockComponent = {
template: '<div class="vue-pane-panel">{{ title }}: {{ params.content }}</div>',
props: ['params', 'api', 'containerApi', 'title']
};
expect(mockComponent.template).toContain('vue-pane-panel');
expect(mockComponent.props).toContain('params');
expect(mockComponent.props).toContain('api');
expect(mockComponent.props).toContain('containerApi');
expect(mockComponent.props).toContain('title');
});
});
describe('VuePaneviewPanelView', () => {
test('should be a class that implements IPanePart interface', () => {
expect(VuePaneviewPanelView).toBeDefined();
expect(typeof VuePaneviewPanelView).toBe('function');
});
test('should create instance with required properties', () => {
const mockVueInstance = {
appContext: { components: {} },
components: {},
parent: null,
};
const mockVueComponent = {
props: { params: Object, api: Object, containerApi: Object, title: String },
template: '<div>{{ title }}: Test Paneview Panel</div>',
} as any;
const panelView = new VuePaneviewPanelView(
'paneview-test-id',
mockVueComponent,
mockVueInstance as any
);
expect(panelView.id).toBe('paneview-test-id');
expect(panelView.element).toBeInstanceOf(HTMLElement);
expect(typeof panelView.init).toBe('function');
expect(typeof panelView.update).toBe('function');
expect(typeof panelView.dispose).toBe('function');
expect(typeof panelView.toJSON).toBe('function');
});
test('should return correct JSON representation', () => {
const mockVueInstance = {
appContext: { components: {} },
components: {},
parent: null,
};
const panelView = new VuePaneviewPanelView(
'paneview-test-id',
{ template: '<div>Test</div>' } as any,
mockVueInstance as any
);
const json = panelView.toJSON();
expect(json).toEqual({ id: 'paneview-test-id' });
});
test('should handle lifecycle methods gracefully', () => {
const mockVueInstance = {
appContext: { components: {} },
components: {},
parent: null,
};
const panelView = new VuePaneviewPanelView(
'test-id',
{ template: '<div>Test</div>' } as any,
mockVueInstance as any
);
expect(() => panelView.update({ params: { data: 'test' } })).not.toThrow();
expect(() => panelView.dispose()).not.toThrow();
});
});

View File

@ -1,76 +0,0 @@
// Import core functionality that we know works
import * as core from 'dockview-core';
// Simple unit tests that verify basic functionality without complex Vue component testing
describe('Vue Components Basic Tests', () => {
test('should be able to import core dockview functionality', () => {
expect(core.createDockview).toBeDefined();
expect(core.createSplitview).toBeDefined();
expect(core.createGridview).toBeDefined();
expect(core.createPaneview).toBeDefined();
});
test('should be able to import APIs', () => {
expect(core.DockviewApi).toBeDefined();
expect(core.SplitviewApi).toBeDefined();
expect(core.GridviewApi).toBeDefined();
expect(core.PaneviewApi).toBeDefined();
});
test('should be able to import orientation enum', () => {
expect(core.Orientation).toBeDefined();
expect(core.Orientation.HORIZONTAL).toBeDefined();
expect(core.Orientation.VERTICAL).toBeDefined();
});
});
// Test view classes - basic import test
describe('Vue View Classes', () => {
test('Vue view classes should be importable', () => {
// Just test that we can import them without errors
expect(() => {
require('../splitview/view');
require('../gridview/view');
require('../paneview/view');
}).not.toThrow();
});
});
// Test utility functions
describe('Utility Functions', () => {
test('should export utility functions', () => {
const utils = require('../utils');
expect(utils.findComponent).toBeDefined();
expect(utils.mountVueComponent).toBeDefined();
expect(utils.VuePart).toBeDefined();
expect(typeof utils.findComponent).toBe('function');
expect(typeof utils.mountVueComponent).toBe('function');
expect(typeof utils.VuePart).toBe('function');
});
test('findComponent should throw when component not found', () => {
const { findComponent } = require('../utils');
const mockInstance = {
components: {},
parent: null,
appContext: {
components: {},
},
};
expect(() => findComponent(mockInstance, 'non-existent')).toThrow(
"Failed to find Vue Component 'non-existent'"
);
});
});
// Test that the package builds correctly
describe('Package Structure', () => {
test('package should build without errors', () => {
// If we get this far, the package structure is correct
expect(true).toBe(true);
});
});

View File

@ -1,124 +0,0 @@
import { createSplitview, Orientation, PROPERTY_KEYS_SPLITVIEW } from 'dockview-core';
import { VueSplitviewPanelView } from '../splitview/view';
describe('SplitviewVue Component', () => {
test('should export component types', () => {
const types = require('../splitview/types');
expect(types).toBeDefined();
expect(typeof types).toBe('object');
});
test('should have access to orientation constants', () => {
expect(Orientation.HORIZONTAL).toBeDefined();
expect(Orientation.VERTICAL).toBeDefined();
});
test('should export dockview-core functionality', () => {
const dockviewCore = require('dockview-core');
expect(dockviewCore.createSplitview).toBeDefined();
expect(dockviewCore.PROPERTY_KEYS_SPLITVIEW).toBeDefined();
});
test('should have correct splitview properties', () => {
expect(PROPERTY_KEYS_SPLITVIEW).toContain('orientation');
expect(PROPERTY_KEYS_SPLITVIEW).toContain('proportionalLayout');
expect(PROPERTY_KEYS_SPLITVIEW).toContain('disableAutoResizing');
});
test('should create splitview with Vue framework support', () => {
// Test that Vue-specific components can be created with proper type safety
expect(typeof createSplitview).toBe('function');
expect(typeof VueSplitviewPanelView).toBe('function');
// Test that a Vue splitview panel view can be instantiated
const mockVueComponent = { template: '<div>Test</div>' } as any;
const mockParent = {} as any;
const panelView = new VueSplitviewPanelView(
'test-id',
'test-component',
mockVueComponent,
mockParent
);
expect(panelView.id).toBe('test-id');
expect(panelView.element).toBeInstanceOf(HTMLElement);
expect(typeof panelView.getComponent).toBe('function');
});
test('should handle Vue component integration', () => {
// Test Vue component factory creation for splitview
const mockComponent = {
template: '<div class="vue-splitview-panel">{{ params.title }}</div>',
props: ['params', 'api', 'containerApi']
};
expect(mockComponent.template).toContain('vue-splitview-panel');
expect(mockComponent.props).toContain('params');
expect(mockComponent.props).toContain('api');
expect(mockComponent.props).toContain('containerApi');
});
});
describe('VueSplitviewPanelView', () => {
test('should be a class that extends SplitviewPanel', () => {
expect(VueSplitviewPanelView).toBeDefined();
expect(typeof VueSplitviewPanelView).toBe('function');
});
test('should create instance with required properties', () => {
const mockVueInstance = {
appContext: { components: {} },
components: {},
parent: null,
};
const mockVueComponent = {
props: { params: Object, api: Object, containerApi: Object },
template: '<div>Test</div>',
} as any;
const panelView = new VueSplitviewPanelView(
'test-id',
'test-component',
mockVueComponent,
mockVueInstance as any
);
expect(panelView.id).toBe('test-id');
expect(panelView.element).toBeInstanceOf(HTMLElement);
expect(typeof panelView.getComponent).toBe('function');
});
test('should handle getComponent with mocked parameters', () => {
const mockVueInstance = {
appContext: { components: {} },
components: {},
parent: null,
};
const mockVueComponent = {
props: { params: Object, api: Object, containerApi: Object },
template: '<div>Test</div>',
} as any;
const panelView = new VueSplitviewPanelView(
'test-id',
'test-component',
mockVueComponent,
mockVueInstance as any
);
// Mock _params to avoid accessor error
(panelView as any)._params = {
params: {},
accessor: { id: 'test-accessor' }
};
const component = panelView.getComponent();
expect(component).toBeDefined();
expect(component.constructor.name).toBe('VuePart');
});
});

View File

@ -1,125 +0,0 @@
import { VuePart, findComponent, mountVueComponent } from '../utils';
describe('Utils', () => {
test('should export VuePart class', () => {
expect(VuePart).toBeDefined();
expect(typeof VuePart).toBe('function');
});
test('should export findComponent function', () => {
expect(findComponent).toBeDefined();
expect(typeof findComponent).toBe('function');
});
test('should export mountVueComponent function', () => {
expect(mountVueComponent).toBeDefined();
expect(typeof mountVueComponent).toBe('function');
});
});
describe('findComponent', () => {
test('should find component in instance components', () => {
const testComponent = { template: '<div>Test</div>' };
const mockInstance = {
components: {
'test-component': testComponent
},
appContext: { components: {} },
parent: null
} as any;
const found = findComponent(mockInstance, 'test-component');
expect(found).toBe(testComponent);
});
test('should find component in app context', () => {
const testComponent = { template: '<div>Test</div>' };
const mockInstance = {
components: {},
appContext: {
components: {
'global-component': testComponent
}
},
parent: null
} as any;
const found = findComponent(mockInstance, 'global-component');
expect(found).toBe(testComponent);
});
test('should throw error when component not found', () => {
const mockInstance = {
components: {},
appContext: { components: {} },
parent: null
} as any;
expect(() => findComponent(mockInstance, 'non-existent')).toThrow(
"Failed to find Vue Component 'non-existent'"
);
});
});
describe('VuePart', () => {
let container: HTMLElement;
let testComponent: any;
let mockParent: any;
let vuePart: VuePart;
beforeEach(() => {
container = document.createElement('div');
testComponent = {
template: '<div class="vue-part">{{ params.title }} - {{ params.data }}</div>',
props: ['params', 'api', 'containerApi']
};
mockParent = {
appContext: {
components: {},
provides: {}
},
provides: {}
};
const mockProps = {
params: { title: 'Test Title', data: 'test data' },
api: { id: 'test-api' },
containerApi: { id: 'container-api' }
};
vuePart = new VuePart(container, testComponent, mockParent, mockProps);
});
test('should create VuePart instance', () => {
expect(vuePart).toBeInstanceOf(VuePart);
expect(vuePart.constructor.name).toBe('VuePart');
});
test('should have required methods', () => {
expect(typeof vuePart.init).toBe('function');
expect(typeof vuePart.update).toBe('function');
expect(typeof vuePart.dispose).toBe('function');
});
test('should handle update before init gracefully', () => {
expect(() => vuePart.update({ params: { title: 'New' } })).not.toThrow();
});
test('should handle dispose before init gracefully', () => {
expect(() => vuePart.dispose()).not.toThrow();
});
test('should handle init call without throwing', () => {
// Test that init can be called without throwing
// Note: may fail due to Vue environment setup but should not crash the test
try {
vuePart.init();
vuePart.dispose();
} catch (error) {
// Vue mounting may fail in test environment, but VuePart should handle it
expect(error).toBeDefined();
}
});
});

View File

@ -1,128 +0,0 @@
import {
ref,
onMounted,
watch,
onBeforeUnmount,
markRaw,
getCurrentInstance,
type ComponentInternalInstance,
} from 'vue';
import { findComponent } from '../utils';
export interface ViewComponentConfig<
TApi,
TOptions,
TProps,
TEvents,
TView,
TFrameworkOptions
> {
componentName: string;
propertyKeys: readonly (keyof TOptions)[];
createApi: (element: HTMLElement, options: TOptions & TFrameworkOptions) => TApi;
createView: (
id: string,
name: string | undefined,
component: any,
instance: ComponentInternalInstance
) => TView;
extractCoreOptions: (props: TProps) => TOptions;
}
export function useViewComponent<
TApi extends { dispose(): void; updateOptions(options: Partial<TOptions>): void; layout(width: number, height: number): void },
TOptions,
TProps,
TEvents,
TView,
TFrameworkOptions
>(
config: ViewComponentConfig<TApi, TOptions, TProps, TEvents, TView, TFrameworkOptions>,
props: TProps,
emit: (event: 'ready', payload: { api: TApi }) => void
) {
const el = ref<HTMLElement | null>(null);
const instance = ref<TApi | null>(null);
config.propertyKeys.forEach((coreOptionKey) => {
watch(
() => (props as any)[coreOptionKey],
(newValue) => {
if (instance.value) {
instance.value.updateOptions({ [coreOptionKey]: newValue } as Partial<TOptions>);
}
}
);
});
watch(
() => (props as any).components,
() => {
if (instance.value) {
const inst = getCurrentInstance();
if (!inst) {
throw new Error(`${config.componentName}: getCurrentInstance() returned null`);
}
instance.value.updateOptions({
createComponent: (options: { id: string; name?: string }) => {
const component = findComponent(inst, options.name!);
return config.createView(
options.id,
options.name,
component! as any,
inst
);
},
} as unknown as Partial<TOptions>);
}
}
);
onMounted(() => {
if (!el.value) {
throw new Error(`${config.componentName}: element is not mounted`);
}
const inst = getCurrentInstance();
if (!inst) {
throw new Error(`${config.componentName}: getCurrentInstance() returned null`);
}
const frameworkOptions = {
createComponent(options: { id: string; name?: string }) {
const component = findComponent(inst, options.name!);
return config.createView(
options.id,
options.name,
component! as any,
inst
);
},
} as TFrameworkOptions;
const api = config.createApi(el.value, {
...config.extractCoreOptions(props),
...frameworkOptions,
});
const { clientWidth, clientHeight } = el.value;
api.layout(clientWidth, clientHeight);
instance.value = markRaw(api) as any;
emit('ready', { api });
});
onBeforeUnmount(() => {
if (instance.value) {
instance.value.dispose();
}
});
return {
el,
instance,
};
}

View File

@ -1,41 +0,0 @@
<script setup lang="ts">
import {
GridviewApi,
type GridviewOptions,
PROPERTY_KEYS_GRIDVIEW,
type GridviewFrameworkOptions,
createGridview,
} from 'dockview-core';
import { defineProps, defineEmits } from 'vue';
import { useViewComponent } from '../composables/useViewComponent';
import { VueGridviewPanelView } from './view';
import type { IGridviewVueProps, GridviewVueEvents } from './types';
function extractCoreOptions(props: IGridviewVueProps): GridviewOptions {
const coreOptions = (PROPERTY_KEYS_GRIDVIEW as (keyof GridviewOptions)[]).reduce(
(obj, key) => {
(obj as any)[key] = props[key];
return obj;
},
{} as Partial<GridviewOptions>
);
return coreOptions as GridviewOptions;
}
const emit = defineEmits<GridviewVueEvents>();
const props = defineProps<IGridviewVueProps>();
const { el } = useViewComponent({
componentName: 'gridview-vue',
propertyKeys: PROPERTY_KEYS_GRIDVIEW,
createApi: createGridview,
createView: (id, name, component, instance) =>
new VueGridviewPanelView(id, name, component, instance),
extractCoreOptions,
}, props, emit);
</script>
<template>
<div ref="el" style="height: 100%; width: 100%" />
</template>

View File

@ -1,23 +0,0 @@
import type {
GridviewApi,
GridviewOptions,
GridviewPanelApi,
} from 'dockview-core';
export interface GridviewReadyEvent {
api: GridviewApi;
}
export interface IGridviewVuePanelProps<T extends Record<string, any> = any> {
params: T;
api: GridviewPanelApi;
containerApi: GridviewApi;
}
export interface IGridviewVueProps extends GridviewOptions {
components: Record<string, string>;
}
export type GridviewVueEvents = {
ready: [event: GridviewReadyEvent];
};

View File

@ -1,34 +0,0 @@
import {
GridviewApi,
GridviewPanel,
IFrameworkPart,
} from 'dockview-core';
import { type ComponentInternalInstance } from 'vue';
import { VuePart, type VueComponent } from '../utils';
import type { IGridviewVuePanelProps } from './types';
export class VueGridviewPanelView extends GridviewPanel {
constructor(
id: string,
component: string,
private readonly vueComponent: VueComponent<IGridviewVuePanelProps>,
private readonly parent: ComponentInternalInstance
) {
super(id, component);
}
getComponent(): IFrameworkPart {
return new VuePart(
this.element,
this.vueComponent,
this.parent,
{
params: this._params?.params ?? {},
api: this.api,
containerApi: new GridviewApi(
(this._params as any).accessor
),
}
);
}
}

View File

@ -1,15 +1,6 @@
export * from 'dockview-core';
import DockviewVue from './dockview/dockview.vue';
import SplitviewVue from './splitview/splitview.vue';
import GridviewVue from './gridview/gridview.vue';
import PaneviewVue from './paneview/paneview.vue';
export { DockviewVue, SplitviewVue, GridviewVue, PaneviewVue };
export { DockviewVue };
export * from './dockview/dockview.vue';
export * from './dockview/types';
export * from './splitview/types';
export * from './gridview/types';
export * from './paneview/types';
export * from './utils';
export type { VueComponent } from './utils';

View File

@ -1,41 +0,0 @@
<script setup lang="ts">
import {
PaneviewApi,
type PaneviewOptions,
PROPERTY_KEYS_PANEVIEW,
type PaneviewFrameworkOptions,
createPaneview,
} from 'dockview-core';
import { defineProps, defineEmits } from 'vue';
import { useViewComponent } from '../composables/useViewComponent';
import { VuePaneviewPanelView } from './view';
import type { IPaneviewVueProps, PaneviewVueEvents } from './types';
function extractCoreOptions(props: IPaneviewVueProps): PaneviewOptions {
const coreOptions = (PROPERTY_KEYS_PANEVIEW as (keyof PaneviewOptions)[]).reduce(
(obj, key) => {
(obj as any)[key] = props[key];
return obj;
},
{} as Partial<PaneviewOptions>
);
return coreOptions as PaneviewOptions;
}
const emit = defineEmits<PaneviewVueEvents>();
const props = defineProps<IPaneviewVueProps>();
const { el } = useViewComponent({
componentName: 'paneview-vue',
propertyKeys: PROPERTY_KEYS_PANEVIEW,
createApi: createPaneview,
createView: (id, name, component, instance) =>
new VuePaneviewPanelView(id, component, instance),
extractCoreOptions,
}, props, emit);
</script>
<template>
<div ref="el" style="height: 100%; width: 100%" />
</template>

View File

@ -1,24 +0,0 @@
import type {
PaneviewApi,
PaneviewOptions,
PaneviewPanelApi,
} from 'dockview-core';
export interface PaneviewReadyEvent {
api: PaneviewApi;
}
export interface IPaneviewVuePanelProps<T extends Record<string, any> = any> {
params: T;
api: PaneviewPanelApi;
containerApi: PaneviewApi;
title: string;
}
export interface IPaneviewVueProps extends PaneviewOptions {
components: Record<string, string>;
}
export type PaneviewVueEvents = {
ready: [event: PaneviewReadyEvent];
};

View File

@ -1,58 +0,0 @@
import {
IPanePart,
PanePanelComponentInitParameter,
PanelUpdateEvent,
} from 'dockview-core';
import { type ComponentInternalInstance } from 'vue';
import { VuePart, type VueComponent } from '../utils';
import type { IPaneviewVuePanelProps } from './types';
export class VuePaneviewPanelView implements IPanePart {
private readonly _element: HTMLElement;
private part?: VuePart<IPaneviewVuePanelProps>;
get element() {
return this._element;
}
constructor(
public readonly id: string,
private readonly vueComponent: VueComponent<IPaneviewVuePanelProps>,
private readonly parent: ComponentInternalInstance
) {
this._element = document.createElement('div');
this._element.style.height = '100%';
this._element.style.width = '100%';
}
public init(parameters: PanePanelComponentInitParameter): void {
this.part = new VuePart(
this.element,
this.vueComponent,
this.parent,
{
params: parameters.params,
api: parameters.api,
title: parameters.title,
containerApi: parameters.containerApi,
}
);
this.part.init();
}
public toJSON() {
return {
id: this.id,
};
}
public update(params: PanelUpdateEvent) {
// The update method for paneview doesn't need to pass all props,
// just the updated params
(this.part as any)?.update({ params: params.params });
}
public dispose() {
this.part?.dispose();
}
}

View File

@ -1,41 +0,0 @@
<script setup lang="ts">
import {
SplitviewApi,
type SplitviewOptions,
PROPERTY_KEYS_SPLITVIEW,
type SplitviewFrameworkOptions,
createSplitview,
} from 'dockview-core';
import { defineProps, defineEmits } from 'vue';
import { useViewComponent } from '../composables/useViewComponent';
import { VueSplitviewPanelView } from './view';
import type { ISplitviewVueProps, SplitviewVueEvents } from './types';
function extractCoreOptions(props: ISplitviewVueProps): SplitviewOptions {
const coreOptions = (PROPERTY_KEYS_SPLITVIEW as (keyof SplitviewOptions)[]).reduce(
(obj, key) => {
(obj as any)[key] = props[key];
return obj;
},
{} as Partial<SplitviewOptions>
);
return coreOptions as SplitviewOptions;
}
const emit = defineEmits<SplitviewVueEvents>();
const props = defineProps<ISplitviewVueProps>();
const { el } = useViewComponent({
componentName: 'splitview-vue',
propertyKeys: PROPERTY_KEYS_SPLITVIEW,
createApi: createSplitview,
createView: (id, name, component, instance) =>
new VueSplitviewPanelView(id, name, component, instance),
extractCoreOptions,
}, props, emit);
</script>
<template>
<div ref="el" style="height: 100%; width: 100%" />
</template>

View File

@ -1,23 +0,0 @@
import type {
SplitviewApi,
SplitviewOptions,
SplitviewPanelApi,
} from 'dockview-core';
export interface SplitviewReadyEvent {
api: SplitviewApi;
}
export interface ISplitviewVuePanelProps<T extends Record<string, any> = any> {
params: T;
api: SplitviewPanelApi;
containerApi: SplitviewApi;
}
export interface ISplitviewVueProps extends SplitviewOptions {
components: Record<string, string>;
}
export type SplitviewVueEvents = {
ready: [event: SplitviewReadyEvent];
};

View File

@ -1,35 +0,0 @@
import {
SplitviewApi,
PanelViewInitParameters,
SplitviewPanel,
IFrameworkPart,
} from 'dockview-core';
import { type ComponentInternalInstance } from 'vue';
import { VuePart, type VueComponent } from '../utils';
import type { ISplitviewVuePanelProps } from './types';
export class VueSplitviewPanelView extends SplitviewPanel {
constructor(
id: string,
component: string,
private readonly vueComponent: VueComponent<ISplitviewVuePanelProps>,
private readonly parent: ComponentInternalInstance
) {
super(id, component);
}
getComponent(): IFrameworkPart {
return new VuePart(
this.element,
this.vueComponent,
this.parent,
{
params: this._params?.params ?? {},
api: this.api,
containerApi: new SplitviewApi(
(this._params as any).accessor
),
}
);
}
}

View File

@ -41,7 +41,7 @@ export function findComponent(
name: string
): VueComponent | null {
let instance = parent as any;
let component: any = null;
let component = null;
while (!component && instance) {
component = instance.components?.[name];
@ -56,7 +56,7 @@ export function findComponent(
throw new Error(`Failed to find Vue Component '${name}'`);
}
return component as VueComponent;
return component;
}
/**
@ -231,35 +231,3 @@ export class VueHeaderActionsRenderer
this._renderDisposable?.dispose();
}
}
export class VuePart<T extends Record<string, any> = any> {
private _renderDisposable:
| { update: (props: any) => void; dispose: () => void }
| undefined;
constructor(
private readonly element: HTMLElement,
private readonly vueComponent: VueComponent<T>,
private readonly parent: ComponentInternalInstance,
private props: T
) {}
init(): void {
this._renderDisposable?.dispose();
this._renderDisposable = mountVueComponent(
this.vueComponent,
this.parent,
this.props,
this.element
);
}
update(props: T): void {
this.props = { ...this.props, ...props };
this._renderDisposable?.update(this.props);
}
dispose(): void {
this._renderDisposable?.dispose();
}
}

View File

@ -1,6 +1,6 @@
{
"name": "dockview",
"version": "4.11.0",
"version": "4.7.1",
"description": "Zero dependency layout manager supporting tabs, grids and splitviews",
"keywords": [
"splitview",
@ -53,7 +53,7 @@
"test:cov": "cross-env ../../node_modules/.bin/jest --selectProjects dockview --coverage"
},
"dependencies": {
"dockview-core": "^4.11.0"
"dockview-core": "^4.7.1"
},
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"

View File

@ -10,11 +10,6 @@ function useTitle(api: DockviewPanelApi): string | undefined {
setTitle(event.title);
});
// Depending on the order in which React effects are run, the title may already be out of sync (cf. issue #1003).
if (title !== api.title) {
setTitle(api.title);
}
return () => {
disposable.dispose();
};

View File

@ -1,17 +0,0 @@
---
slug: dockview-4.8.0-release
title: Dockview 4.8.0
tags: [release]
---
# Release Notes
Please reference docs @ [dockview.dev](https://dockview.dev).
## 🚀 Features
- Angular framework wrapper support [#1001](https://github.com/mathuo/dockview/pull/1001)
## 🛠 Miscs
## 🔥 Breaking changes

View File

@ -1,22 +0,0 @@
---
slug: dockview-4.10.0-release
title: Dockview 4.10.0
tags: [release]
---
# Release Notes
Please reference docs @ [dockview.dev](https://dockview.dev).
## 🚀 Features
- Template docs [#1027](https://github.com/mathuo/dockview/pull/1027)
- Angular docs
- Enhanced Vue component tests with comprehensive DOM and API testing [#1024](https://github.com/mathuo/dockview/pull/1024)
## 🛠 Miscs
- Bug: Fix registerPanel timing to prevent undefined API errors [#1026](https://github.com/mathuo/dockview/pull/1026)
- Bug: Fix title possibly out of sync in DockviewDefaultTab [#1015](https://github.com/mathuo/dockview/pull/1015)
## 🔥 Breaking changes

View File

@ -1,17 +0,0 @@
---
slug: dockview-4.11.0-release
title: Dockview 4.11.0
tags: [release]
---
# Release Notes
Please reference docs @ [dockview.dev](https://dockview.dev).
## 🚀 Features
## 🛠 Miscs
- Bug: Fix constraints persistence and precedence issues [#967](https://github.com/mathuo/dockview/pull/967)
## 🔥 Breaking changes

View File

@ -1,6 +1,6 @@
{
"name": "dockview-docs",
"version": "4.11.0",
"version": "4.8.0",
"private": true,
"scripts": {
"build": "npm run build-templates && docusaurus build",
@ -38,7 +38,7 @@
"ag-grid-react": "^31.0.2",
"axios": "^1.6.3",
"clsx": "^2.1.0",
"dockview": "^4.11.0",
"dockview": "^4.7.1",
"prism-react-renderer": "^2.3.1",
"react-dnd": "^16.0.1",
"react-laag": "^2.0.5",

View File

@ -1,7 +1,6 @@
import fs from 'fs-extra';
import * as path from 'path';
import { argv } from 'process';
import { execSync } from 'child_process';
import { fileURLToPath } from 'url';
@ -85,13 +84,12 @@ const IMPORTS_PATHS = {
},
typescript: {},
angular: {
'@angular/core': `https://esm.sh/@angular/core@${ANGULAR_VERSION}?target=es2018`,
'@angular/common': `https://esm.sh/@angular/common@${ANGULAR_VERSION}?target=es2018`,
'@angular/platform-browser-dynamic': `https://esm.sh/@angular/platform-browser-dynamic@${ANGULAR_VERSION}?target=es2018`,
'@angular/platform-browser': `https://esm.sh/@angular/platform-browser@${ANGULAR_VERSION}?target=es2018`,
'@angular/compiler': `https://esm.sh/@angular/compiler@${ANGULAR_VERSION}?target=es2018`,
rxjs: `https://esm.sh/rxjs@7.8.1?target=es2018`,
'zone.js': `https://esm.sh/zone.js@0.14.3?target=es2018`,
'@angular/core': `https://esm.sh/@angular/core@${ANGULAR_VERSION}`,
'@angular/common': `https://esm.sh/@angular/common@${ANGULAR_VERSION}`,
'@angular/platform-browser-dynamic': `https://esm.sh/@angular/platform-browser-dynamic@${ANGULAR_VERSION}`,
'@angular/platform-browser': `https://esm.sh/@angular/platform-browser@${ANGULAR_VERSION}`,
'rxjs': `https://esm.sh/rxjs@7.8.1`,
'zone.js': `https://esm.sh/zone.js@0.14.3`,
},
};
@ -99,54 +97,6 @@ const template = fs
.readFileSync(path.join(__dirname, './template.html'))
.toString();
async function compileAngularTypeScript(inputPath, outputPath) {
try {
// Import TypeScript compiler
const ts = await import('typescript');
// Copy directory structure first
fs.copySync(inputPath, outputPath);
// Find all .ts files and transpile them
const tsFiles = fs
.readdirSync(outputPath)
.filter((file) => file.endsWith('.ts'));
for (const file of tsFiles) {
const filePath = path.join(outputPath, file);
const source = fs.readFileSync(filePath, 'utf8');
// Transpile with decorator support, keeping imports as-is
const result = ts.default.transpile(source, {
target: ts.default.ScriptTarget.ES5, // Use ES5 to ensure maximum compatibility
module: ts.default.ModuleKind.System,
experimentalDecorators: true,
emitDecoratorMetadata: true,
esModuleInterop: true,
allowSyntheticDefaultImports: true,
strict: false,
skipLibCheck: true,
noResolve: true, // Don't try to resolve imports
useDefineForClassFields: false, // Avoid class field initialization issues
downlevelIteration: true, // Ensure iterators work in older JS
});
// Write the transpiled file
fs.writeFileSync(filePath, result);
}
return true;
} catch (error) {
console.warn(
`Failed to compile Angular TypeScript for ${inputPath}:`,
error.message
);
// Fall back to copying source files as-is
fs.copySync(inputPath, outputPath);
return false;
}
}
function createIndexHTML(options) {
return template
.replace('{{title}}', options.title)
@ -157,113 +107,80 @@ function createIndexHTML(options) {
.join(',\n')}`
)
.replace('{{app}}', options.app)
.replace('{{appElement}}', options.appElement || '')
.replace('{{githubLink}}', options.githubUrl)
.replace('{{codeSandboxLink}}', options.codeSandboxUrl);
.replace('{{codeSandboxLink}}', options.codeSandboxUrl)
}
const input_dir = path.join(__dirname, '../templates');
const output = path.join(__dirname, '../static/templates');
const COMPONENTS = ['dockview', 'splitview', 'gridview', 'paneview'];
const COMPONENTS = ['dockview'];
const FRAMEWORKS = ['react', 'vue', 'typescript', 'angular'];
const list = [];
const githubUrl =
'https://github.com/mathuo/dockview/tree/master/packages/docs/templates';
const codeSandboxUrl =
'https://codesandbox.io/p/sandbox/github/mathuo/dockview/tree/gh-pages/templates';
const githubUrl = "https://github.com/mathuo/dockview/tree/master/packages/docs/templates"
const codeSandboxUrl = "https://codesandbox.io/p/sandbox/github/mathuo/dockview/tree/gh-pages/templates"
async function buildTemplates() {
for (const component of COMPONENTS) {
const componentDir = path.join(input_dir, component);
for (const component of COMPONENTS) {
const componentDir = path.join(input_dir, component);
const templates = fs.readdirSync(componentDir);
const templates = fs.readdirSync(componentDir);
for (const folder of templates) {
for (const framework of FRAMEWORKS) {
if (
!fs.existsSync(
path.join(componentDir, folder, framework, 'src')
)
) {
continue;
}
const srcPath = path.join(
componentDir,
folder,
framework,
'src'
);
const destPath = path.join(
output,
component,
folder,
framework,
'src'
);
if (framework === 'angular') {
// Compile Angular TypeScript files with decorator support
await compileAngularTypeScript(srcPath, destPath);
} else {
// Copy other frameworks as-is
fs.copySync(srcPath, destPath);
}
const templateGithubUrl = `${githubUrl}/${component}/${folder}/${framework}/src`;
const templateCodeSandboxUrl = `${codeSandboxUrl}/${component}/${folder}/${framework}`;
const template = createIndexHTML({
title: `Dockview | ${folder} ${framework}`,
app:
framework === 'react'
? './src/index.tsx'
: framework === 'angular'
? './src/index.ts'
: './src/index.ts',
appElement:
framework === 'angular' ? '<app-root></app-root>' : '',
importPaths: {
...IMPORTS_PATHS[framework],
...DOCKVIEW_CDN[framework][
USE_LOCAL_CDN ? 'local' : 'remote'
],
},
githubUrl: templateGithubUrl,
codeSandboxUrl: templateCodeSandboxUrl,
});
fs.writeFileSync(
path.join(
output,
component,
folder,
framework,
'index.html'
),
template
);
list.push({
name: `${component}/${framework}/${folder}`,
path: path.join(component, folder, framework, 'index.html'),
});
for (const folder of templates) {
for (const framework of FRAMEWORKS) {
if (
!fs.existsSync(
path.join(componentDir, folder, framework, 'src')
)
) {
continue;
}
fs.copySync(
path.join(componentDir, folder, framework, 'src'),
path.join(output, component, folder, framework, 'src')
);
const templateGithubUrl = `${githubUrl}/${component}/${folder}/${framework}/src`
const templateCodeSandboxUrl = `${codeSandboxUrl}/${component}/${folder}/${framework}`
const template = createIndexHTML({
title: `Dockview | ${folder} ${framework}`,
app:
framework === 'react'
? './src/index.tsx'
: framework === 'angular'
? './src/index.ts'
: './src/index.ts',
importPaths: {
...IMPORTS_PATHS[framework],
...DOCKVIEW_CDN[framework][
USE_LOCAL_CDN ? 'local' : 'remote'
],
},
githubUrl: templateGithubUrl,
codeSandboxUrl: templateCodeSandboxUrl
});
fs.writeFileSync(
path.join(output, component, folder, framework, 'index.html'),
template
);
list.push({
name: `${framework}/${folder}`,
path: path.join(component, folder, framework, 'index.html'),
});
}
}
const index = `<div>${list
.sort((a, b) => a.name.localeCompare(b.name))
.map((item) => {
return `<div><a href=${`/templates/${item.path}`}>${
item.name
}</a></div>`;
})
.join('\n')}</div>`;
fs.writeFileSync(path.join(output, 'index.html'), index);
}
// Run the build
buildTemplates().catch(console.error);
const index = `<div>${list
.sort((a, b) => a.name.localeCompare(b.name))
.map((item) => {
return `<div><a href=${`/templates/${item.path}`}>${
item.name
}</a></div>`;
})
.join('\n')}</div>`;
fs.writeFileSync(path.join(output, 'index.html'), index);

View File

@ -96,7 +96,7 @@
</button>
</a>
</div>
<div id="app">{{appElement}}</div>
<div id="app"></div>
</div>
<script type="systemjs-module" src="import:{{app}}"></script>
<object

View File

@ -1,22 +1,17 @@
import 'zone.js';
import '@angular/compiler';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { Component, Type, NgModule, Input } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { DockviewAngularModule } from 'dockview-angular';
import { bootstrapApplication } from '@angular/platform-browser';
import { Component, Type } from '@angular/core';
import { DockviewAngularComponent } from 'dockview-angular';
import 'dockview-core/dist/styles/dockview.css';
// Default panel component
@Component({
selector: 'default-panel',
template: `<div>{{ title || 'Default Panel' }}</div>`
template: `<div>{{ api.title }}</div>`,
standalone: true,
})
export class DefaultPanelComponent {
@Input() api: any;
get title() {
return this.api?.title || this.api?.id || 'Panel';
}
api: any;
constructor() {}
}
@ -32,7 +27,9 @@ export class DefaultPanelComponent {
(ready)="onReady($event)">
</dv-dockview>
</div>
`
`,
standalone: true,
imports: [DockviewAngularComponent]
})
export class AppComponent {
components: Record<string, Type<any>>;
@ -81,14 +78,5 @@ export class AppComponent {
}
}
// App module
@NgModule({
declarations: [AppComponent, DefaultPanelComponent],
imports: [BrowserModule, DockviewAngularModule],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
// Bootstrap the application with JIT compilation
platformBrowserDynamic().bootstrapModule(AppModule).catch(err => console.error(err));
// Bootstrap the application
bootstrapApplication(AppComponent).catch(err => console.error(err));

View File

@ -42,7 +42,7 @@ export const App: React.FC = (props: { theme?: string }) => {
id: 'panel_2',
component: 'default',
title: 'Panel 2',
position: { referencePanel: panel.id },
position: { referencePanel: panel },
});
const panel3 = event.api.addPanel({
@ -50,14 +50,14 @@ export const App: React.FC = (props: { theme?: string }) => {
component: 'default',
title: 'Panel 3',
position: { referencePanel: panel.id, direction: 'right' },
position: { referencePanel: panel, direction: 'right' },
});
const panel4 = event.api.addPanel({
id: 'panel_4',
component: 'default',
title: 'Panel 4',
position: { referencePanel: panel3.id },
position: { referencePanel: panel3 },
});
};

View File

@ -1,76 +0,0 @@
import 'zone.js';
import '@angular/compiler';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { Component, Type, NgModule, Input } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { DockviewAngularModule } from 'dockview-angular';
import 'dockview-core/dist/styles/dockview.css';
@Component({
selector: 'default-panel',
template: `<div style="padding: 10px; color: white; background: #1e1e1e; border: 1px solid #333; height: 100%;">Panel {{ api?.id || 'Unknown' }}</div>`
})
export class DefaultPanelComponent {
@Input() api: any;
constructor() {}
}
@Component({
selector: 'app-root',
template: `
<div style="height: 100vh;">
<dv-gridview
[components]="components"
className="dockview-theme-abyss"
(ready)="onReady($event)">
</dv-gridview>
</div>
`
})
export class AppComponent {
components: Record<string, Type<any>>;
constructor() {
this.components = {
default: DefaultPanelComponent,
};
}
onReady(event: any) {
const api = event.api;
const panel1 = api.addPanel({
id: 'panel_1',
component: 'default',
});
const panel2 = api.addPanel({
id: 'panel_2',
component: 'default',
position: { referencePanel: panel1.id, direction: 'right' },
});
api.addPanel({
id: 'panel_3',
component: 'default',
position: { referencePanel: panel1.id, direction: 'below' },
});
api.addPanel({
id: 'panel_4',
component: 'default',
position: { referencePanel: panel2.id, direction: 'below' },
});
}
}
@NgModule({
declarations: [AppComponent, DefaultPanelComponent],
imports: [BrowserModule, DockviewAngularModule],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
platformBrowserDynamic().bootstrapModule(AppModule).catch(err => console.error(err));

View File

@ -1,59 +0,0 @@
import {
GridviewReact,
GridviewReadyEvent,
IGridviewPanelProps,
} from 'dockview';
import React from 'react';
const Default = (props: IGridviewPanelProps) => {
return (
<div style={{
padding: '10px',
color: 'white',
background: '#1e1e1e',
border: '1px solid #333',
height: '100%'
}}>
Panel {props.api.id}
</div>
);
};
const components = {
default: Default,
};
export default () => {
const onReady = (event: GridviewReadyEvent) => {
const panel1 = event.api.addPanel({
id: 'panel_1',
component: 'default',
});
const panel2 = event.api.addPanel({
id: 'panel_2',
component: 'default',
position: { referencePanel: panel1, direction: 'right' },
});
event.api.addPanel({
id: 'panel_3',
component: 'default',
position: { referencePanel: panel1, direction: 'below' },
});
event.api.addPanel({
id: 'panel_4',
component: 'default',
position: { referencePanel: panel2, direction: 'below' },
});
};
return (
<GridviewReact
className={'dockview-theme-abyss'}
onReady={onReady}
components={components}
/>
);
};

View File

@ -1,7 +0,0 @@
import React from 'react';
import { createRoot } from 'react-dom/client';
import App from './app';
const container = document.getElementById('app')!;
const root = createRoot(container);
root.render(<App />);

View File

@ -1,60 +0,0 @@
import 'dockview-core/dist/styles/dockview.css';
import {
createGridview,
IGridviewPanelProps,
IContentRenderer,
themeAbyss,
} from 'dockview-core';
class Panel implements IContentRenderer {
private readonly _element: HTMLElement;
get element(): HTMLElement {
return this._element;
}
constructor() {
this._element = document.createElement('div');
this._element.style.color = 'white';
this._element.style.padding = '10px';
this._element.style.background = '#1e1e1e';
this._element.style.border = '1px solid #333';
}
init(parameters: IGridviewPanelProps): void {
this._element.textContent = `Panel ${parameters.id}`;
}
}
const api = createGridview(document.getElementById('app'), {
theme: themeAbyss,
createComponent: (options) => {
switch (options.name) {
case 'default':
return new Panel();
}
},
});
const panel1 = api.addPanel({
id: 'panel_1',
component: 'default',
});
const panel2 = api.addPanel({
id: 'panel_2',
component: 'default',
position: { referencePanel: panel1.id, direction: 'right' },
});
api.addPanel({
id: 'panel_3',
component: 'default',
position: { referencePanel: panel1.id, direction: 'below' },
});
api.addPanel({
id: 'panel_4',
component: 'default',
position: { referencePanel: panel2.id, direction: 'below' },
});

View File

@ -1,76 +0,0 @@
import 'dockview-vue/dist/styles/dockview.css';
import { PropType, createApp, defineComponent } from 'vue';
import {
GridviewVue,
GridviewReadyEvent,
IGridviewPanelProps,
} from 'dockview-vue';
const Panel = defineComponent({
name: 'Panel',
props: {
params: {
type: Object as PropType<IGridviewPanelProps>,
required: true,
},
},
data() {
return {
id: '',
};
},
mounted() {
this.id = this.params.api.id;
},
template: `
<div style="height: 100%; padding: 10px; color: white; background: #1e1e1e; border: 1px solid #333;">
Panel {{ id }}
</div>`,
});
const App = defineComponent({
name: 'App',
components: {
'gridview-vue': GridviewVue,
panel: Panel,
},
methods: {
onReady(event: GridviewReadyEvent) {
const panel1 = event.api.addPanel({
id: 'panel_1',
component: 'panel',
});
const panel2 = event.api.addPanel({
id: 'panel_2',
component: 'panel',
position: { referencePanel: panel1, direction: 'right' },
});
event.api.addPanel({
id: 'panel_3',
component: 'panel',
position: { referencePanel: panel1, direction: 'below' },
});
event.api.addPanel({
id: 'panel_4',
component: 'panel',
position: { referencePanel: panel2, direction: 'below' },
});
},
},
template: `
<gridview-vue
style="width: 100%; height: 100%"
class="dockview-theme-abyss"
@ready="onReady"
>
</gridview-vue>`,
});
const app = createApp(App);
app.config.errorHandler = (err) => {
console.log(err);
};
app.mount(document.getElementById('app')!);

View File

@ -1,75 +0,0 @@
import 'zone.js';
import '@angular/compiler';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { Component, Type, NgModule, Input } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { DockviewAngularModule } from 'dockview-angular';
import 'dockview-core/dist/styles/dockview.css';
@Component({
selector: 'default-panel',
template: `<div style="padding: 10px; color: white; background: #1e1e1e; border: 1px solid #333; height: 100%;">Panel {{ api?.id || 'Unknown' }}</div>`
})
export class DefaultPanelComponent {
@Input() api: any;
constructor() {}
}
@Component({
selector: 'app-root',
template: `
<div style="height: 100vh;">
<dv-paneview
[components]="components"
className="dockview-theme-abyss"
orientation="vertical"
(ready)="onReady($event)">
</dv-paneview>
</div>
`
})
export class AppComponent {
components: Record<string, Type<any>>;
constructor() {
this.components = {
default: DefaultPanelComponent,
};
}
onReady(event: any) {
const api = event.api;
api.addPanel({
id: 'panel_1',
component: 'default',
title: 'Panel 1',
size: 150,
});
api.addPanel({
id: 'panel_2',
component: 'default',
title: 'Panel 2',
size: 200,
});
api.addPanel({
id: 'panel_3',
component: 'default',
title: 'Panel 3',
size: 180,
});
}
}
@NgModule({
declarations: [AppComponent, DefaultPanelComponent],
imports: [BrowserModule, DockviewAngularModule],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
platformBrowserDynamic().bootstrapModule(AppModule).catch(err => console.error(err));

View File

@ -1,58 +0,0 @@
import {
PaneviewReact,
PaneviewReadyEvent,
IPaneviewPanelProps,
} from 'dockview';
import React from 'react';
const Default = (props: IPaneviewPanelProps) => {
return (
<div style={{
padding: '10px',
color: 'white',
background: '#1e1e1e',
border: '1px solid #333',
height: '100%'
}}>
Panel {props.api.id}
</div>
);
};
const components = {
default: Default,
};
export default () => {
const onReady = (event: PaneviewReadyEvent) => {
event.api.addPanel({
id: 'panel_1',
component: 'default',
title: 'Panel 1',
size: 150,
});
event.api.addPanel({
id: 'panel_2',
component: 'default',
title: 'Panel 2',
size: 200,
});
event.api.addPanel({
id: 'panel_3',
component: 'default',
title: 'Panel 3',
size: 180,
});
};
return (
<PaneviewReact
className={'dockview-theme-abyss'}
orientation="vertical"
onReady={onReady}
components={components}
/>
);
};

View File

@ -1,7 +0,0 @@
import React from 'react';
import { createRoot } from 'react-dom/client';
import App from './app';
const container = document.getElementById('app')!;
const root = createRoot(container);
root.render(<App />);

View File

@ -1,59 +0,0 @@
import 'dockview-core/dist/styles/dockview.css';
import {
createPaneview,
IPaneviewPanelProps,
IContentRenderer,
themeAbyss,
} from 'dockview-core';
class Panel implements IContentRenderer {
private readonly _element: HTMLElement;
get element(): HTMLElement {
return this._element;
}
constructor() {
this._element = document.createElement('div');
this._element.style.color = 'white';
this._element.style.padding = '10px';
this._element.style.background = '#1e1e1e';
this._element.style.border = '1px solid #333';
}
init(parameters: IPaneviewPanelProps): void {
this._element.textContent = `Panel ${parameters.id}`;
}
}
const api = createPaneview(document.getElementById('app'), {
theme: themeAbyss,
orientation: 'vertical',
createComponent: (options) => {
switch (options.name) {
case 'default':
return new Panel();
}
},
});
api.addPanel({
id: 'panel_1',
component: 'default',
title: 'Panel 1',
size: 150,
});
api.addPanel({
id: 'panel_2',
component: 'default',
title: 'Panel 2',
size: 200,
});
api.addPanel({
id: 'panel_3',
component: 'default',
title: 'Panel 3',
size: 180,
});

View File

@ -1,77 +0,0 @@
import 'dockview-vue/dist/styles/dockview.css';
import { PropType, createApp, defineComponent } from 'vue';
import {
PaneviewVue,
PaneviewReadyEvent,
IPaneviewPanelProps,
} from 'dockview-vue';
const Panel = defineComponent({
name: 'Panel',
props: {
params: {
type: Object as PropType<IPaneviewPanelProps>,
required: true,
},
},
data() {
return {
id: '',
title: '',
};
},
mounted() {
this.id = this.params.api.id;
this.title = this.params.api.title;
},
template: `
<div style="height: 100%; padding: 10px; color: white; background: #1e1e1e; border: 1px solid #333;">
Panel {{ id }}
</div>`,
});
const App = defineComponent({
name: 'App',
components: {
'paneview-vue': PaneviewVue,
panel: Panel,
},
methods: {
onReady(event: PaneviewReadyEvent) {
event.api.addPanel({
id: 'panel_1',
component: 'panel',
title: 'Panel 1',
size: 150,
});
event.api.addPanel({
id: 'panel_2',
component: 'panel',
title: 'Panel 2',
size: 200,
});
event.api.addPanel({
id: 'panel_3',
component: 'panel',
title: 'Panel 3',
size: 180,
});
},
},
template: `
<paneview-vue
style="width: 100%; height: 100%"
class="dockview-theme-abyss"
orientation="vertical"
@ready="onReady"
>
</paneview-vue>`,
});
const app = createApp(App);
app.config.errorHandler = (err) => {
console.log(err);
};
app.mount(document.getElementById('app')!);

View File

@ -1,72 +0,0 @@
import 'zone.js';
import '@angular/compiler';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { Component, Type, NgModule, Input } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { DockviewAngularModule } from 'dockview-angular';
import 'dockview-core/dist/styles/dockview.css';
@Component({
selector: 'default-panel',
template: `<div style="padding: 10px; color: white; background: #1e1e1e;">Panel {{ api?.id || 'Unknown' }}</div>`
})
export class DefaultPanelComponent {
@Input() api: any;
constructor() {}
}
@Component({
selector: 'app-root',
template: `
<div style="height: 100vh;">
<dv-splitview
[components]="components"
className="dockview-theme-abyss"
orientation="horizontal"
(ready)="onReady($event)">
</dv-splitview>
</div>
`
})
export class AppComponent {
components: Record<string, Type<any>>;
constructor() {
this.components = {
default: DefaultPanelComponent,
};
}
onReady(event: any) {
const api = event.api;
api.addPanel({
id: 'panel_1',
component: 'default',
size: 200,
});
api.addPanel({
id: 'panel_2',
component: 'default',
size: 300,
});
api.addPanel({
id: 'panel_3',
component: 'default',
size: 200,
});
}
}
@NgModule({
declarations: [AppComponent, DefaultPanelComponent],
imports: [BrowserModule, DockviewAngularModule],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
platformBrowserDynamic().bootstrapModule(AppModule).catch(err => console.error(err));

View File

@ -1,49 +0,0 @@
import {
SplitviewReact,
SplitviewReadyEvent,
ISplitviewPanelProps,
} from 'dockview';
import React from 'react';
const Default = (props: ISplitviewPanelProps) => {
return (
<div style={{ padding: '10px', color: 'white', background: '#1e1e1e' }}>
Panel {props.api.id}
</div>
);
};
const components = {
default: Default,
};
export default () => {
const onReady = (event: SplitviewReadyEvent) => {
event.api.addPanel({
id: 'panel_1',
component: 'default',
size: 200,
});
event.api.addPanel({
id: 'panel_2',
component: 'default',
size: 300,
});
event.api.addPanel({
id: 'panel_3',
component: 'default',
size: 200,
});
};
return (
<SplitviewReact
className={'dockview-theme-abyss'}
orientation="horizontal"
onReady={onReady}
components={components}
/>
);
};

View File

@ -1,7 +0,0 @@
import React from 'react';
import { createRoot } from 'react-dom/client';
import App from './app';
const container = document.getElementById('app')!;
const root = createRoot(container);
root.render(<App />);

View File

@ -1,55 +0,0 @@
import 'dockview-core/dist/styles/dockview.css';
import {
createSplitview,
ISplitviewPanelProps,
IContentRenderer,
themeAbyss,
} from 'dockview-core';
class Panel implements IContentRenderer {
private readonly _element: HTMLElement;
get element(): HTMLElement {
return this._element;
}
constructor() {
this._element = document.createElement('div');
this._element.style.color = 'white';
this._element.style.padding = '10px';
this._element.style.background = '#1e1e1e';
}
init(parameters: ISplitviewPanelProps): void {
this._element.textContent = `Panel ${parameters.id}`;
}
}
const api = createSplitview(document.getElementById('app'), {
theme: themeAbyss,
orientation: 'horizontal',
createComponent: (options) => {
switch (options.name) {
case 'default':
return new Panel();
}
},
});
api.addPanel({
id: 'panel_1',
component: 'default',
size: 200,
});
api.addPanel({
id: 'panel_2',
component: 'default',
size: 300,
});
api.addPanel({
id: 'panel_3',
component: 'default',
size: 200,
});

View File

@ -1,72 +0,0 @@
import 'dockview-vue/dist/styles/dockview.css';
import { PropType, createApp, defineComponent } from 'vue';
import {
SplitviewVue,
SplitviewReadyEvent,
ISplitviewPanelProps,
} from 'dockview-vue';
const Panel = defineComponent({
name: 'Panel',
props: {
params: {
type: Object as PropType<ISplitviewPanelProps>,
required: true,
},
},
data() {
return {
id: '',
};
},
mounted() {
this.id = this.params.api.id;
},
template: `
<div style="height: 100%; padding: 10px; color: white; background: #1e1e1e;">
Panel {{ id }}
</div>`,
});
const App = defineComponent({
name: 'App',
components: {
'splitview-vue': SplitviewVue,
panel: Panel,
},
methods: {
onReady(event: SplitviewReadyEvent) {
event.api.addPanel({
id: 'panel_1',
component: 'panel',
size: 200,
});
event.api.addPanel({
id: 'panel_2',
component: 'panel',
size: 300,
});
event.api.addPanel({
id: 'panel_3',
component: 'panel',
size: 200,
});
},
},
template: `
<splitview-vue
style="width: 100%; height: 100%"
class="dockview-theme-abyss"
orientation="horizontal"
@ready="onReady"
>
</splitview-vue>`,
});
const app = createApp(App);
app.config.errorHandler = (err) => {
console.log(err);
};
app.mount(document.getElementById('app')!);

1981
yarn.lock

File diff suppressed because it is too large Load Diff