feat: Vue 3 Support

This commit is contained in:
mathuo 2024-03-04 20:28:25 +00:00
parent 45919ff397
commit c5770d4381
No known key found for this signature in database
GPG Key ID: C6EEDEFD6CA07281
117 changed files with 6276 additions and 1274 deletions

View File

@ -7,7 +7,8 @@
"esbenp.prettier-vscode", "esbenp.prettier-vscode",
"redhat.vscode-yaml", "redhat.vscode-yaml",
"dbaeumer.vscode-eslint", "dbaeumer.vscode-eslint",
"editorconfig.editorconfig" "editorconfig.editorconfig",
"vue.volar"
], ],
// List of extensions recommended by VS Code that should not be recommended for users of this workspace. // List of extensions recommended by VS Code that should not be recommended for users of this workspace.
"unwantedRecommendations": [] "unwantedRecommendations": []

View File

@ -45,6 +45,8 @@
"@types/react-dom": "^18.2.18", "@types/react-dom": "^18.2.18",
"@typescript-eslint/eslint-plugin": "^6.17.0", "@typescript-eslint/eslint-plugin": "^6.17.0",
"@typescript-eslint/parser": "^6.17.0", "@typescript-eslint/parser": "^6.17.0",
"@vitejs/plugin-vue": "^5.0.4",
"@vue/tsconfig": "^0.5.1",
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"eslint": "^8.56.0", "eslint": "^8.56.0",
"fs-extra": "^11.2.0", "fs-extra": "^11.2.0",
@ -66,9 +68,15 @@
"ts-node": "^10.9.2", "ts-node": "^10.9.2",
"tslib": "^2.6.2", "tslib": "^2.6.2",
"typedoc": "^0.25.6", "typedoc": "^0.25.6",
"typescript": "^5.3.3" "typescript": "^5.3.3",
"vite": "^5.1.5",
"vue": "^3.4.21",
"vue-tsc": "^2.0.5"
}, },
"engines": { "engines": {
"node": ">=18.0" "node": ">=18.0"
},
"dependencies": {
"ag-grid-vue3": "^31.1.1"
} }
} }

View File

@ -0,0 +1,56 @@
<div align="center">
<h1>dockview</h1>
<p>Zero dependency layout manager supporting tabs, groups, grids and splitviews with ReactJS support written in TypeScript</p>
</div>
---
[![npm version](https://badge.fury.io/js/dockview.svg)](https://www.npmjs.com/package/dockview)
[![npm](https://img.shields.io/npm/dm/dockview)](https://www.npmjs.com/package/dockview)
[![CI Build](https://github.com/mathuo/dockview/workflows/CI/badge.svg)](https://github.com/mathuo/dockview/actions?query=workflow%3ACI)
[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=mathuo_dockview&metric=coverage)](https://sonarcloud.io/summary/overall?id=mathuo_dockview)
[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=mathuo_dockview&metric=alert_status)](https://sonarcloud.io/summary/overall?id=mathuo_dockview)
[![Bundle Phobia](https://badgen.net/bundlephobia/minzip/dockview)](https://bundlephobia.com/result?p=dockview)
##
Please see the website: https://dockview.dev
## Features
- Serialization / deserialization with full layout management
- Support for split-views, grid-views and 'dockable' views
- Themeable and customizable
- Tab and Group docking / Drag n' Drop
- Popout Windows
- Floating Groups
- Extensive API
- Supports Shadow DOMs
- High test coverage
- Documentation website with live examples
- Transparent builds and Code Analysis
- Security at mind - verifed publishing and builds through GitHub Actions
Want to verify our builds? Go [here](https://www.npmjs.com/package/dockview#Provenance).
## Quick start
Dockview has a peer dependency on `react >= 16.8.0` and `react-dom >= 16.8.0`. You can install dockview from [npm](https://www.npmjs.com/package/dockview).
```
npm install --save dockview
```
Within your project you must import or reference the stylesheet at `dockview/dist/styles/dockview.css` and attach a theme.
```css
@import '~dockview/dist/styles/dockview.css';
```
You should also attach a dockview theme to an element containing your components. For example:
```html
<body classname="dockview-theme-dark"></body>
```

View File

@ -0,0 +1,6 @@
const gulp = require('gulp');
const buildfile = require('../../scripts/build');
buildfile.init();
gulp.task('run', gulp.series(['sass']));

View File

@ -0,0 +1,34 @@
import { JestConfigWithTsJest } from 'ts-jest';
const config: JestConfigWithTsJest = {
preset: 'ts-jest',
roots: ['<rootDir>/packages/dockview-angular'],
modulePaths: ['<rootDir>/packages/dockview-angular/src'],
displayName: { name: 'dockview', color: 'blue' },
rootDir: '../../',
collectCoverageFrom: [
'<rootDir>/packages/dockview-angular/src/**/*.{js,jsx,ts,tsx}',
],
setupFiles: [
// '<rootDir>/packages/dockview-angular/src/__tests__/__mocks__/resizeObserver.js',
],
setupFilesAfterEnv: ['<rootDir>/jest-setup.ts'],
coveragePathIgnorePatterns: ['/node_modules/'],
modulePathIgnorePatterns: [
// '<rootDir>/packages/dockview-angular/src/__tests__/__mocks__',
// '<rootDir>/packages/dockview-angular/src/__tests__/__test_utils__',
],
coverageDirectory: '<rootDir>/packages/dockview-angular/coverage/',
testResultsProcessor: 'jest-sonar-reporter',
testEnvironment: 'jsdom',
transform: {
'^.+\\.tsx?$': [
'ts-jest',
{
tsconfig: '<rootDir>/tsconfig.test.json',
},
],
},
};
export default config;

View File

@ -0,0 +1,59 @@
{
"name": "dockview-angular",
"version": "0.0.0-beta-0",
"description": "Zero dependency layout manager supporting tabs, grids and splitviews with ReactJS support",
"keywords": [
"splitview",
"split-view",
"gridview",
"grid-view",
"dockview",
"dock-view",
"grid",
"tabs",
"layout",
"layout manager",
"dock layout",
"dock",
"docking",
"splitter",
"drag-and-drop",
"drag",
"drop",
"react",
"react-component"
],
"homepage": "https://github.com/mathuo/dockview",
"bugs": {
"url": "https://github.com/mathuo/dockview/issues"
},
"repository": {
"type": "git",
"url": "https://github.com/mathuo/dockview.git"
},
"license": "MIT",
"author": "https://github.com/mathuo",
"main": "./dist/cjs/index.js",
"module": "./dist/esm/index.js",
"types": "./dist/cjs/index.d.ts",
"files": [
"dist",
"README.md"
],
"scripts": {
"build": "npm run build:package && npm run build:bundles",
"build:bundles": "rollup -c",
"build:cjs": "cross-env ../../node_modules/.bin/tsc --build ./tsconfig.json --verbose --extendedDiagnostics",
"build:css": "gulp sass",
"build:esm": "cross-env ../../node_modules/.bin/tsc --build ./tsconfig.esm.json --verbose --extendedDiagnostics",
"build:package": "npm run build:cjs && npm run build:esm && npm run build:css",
"clean": "rimraf dist/ .build/ .rollup.cache/",
"prepublishOnly": "npm run rebuild && npm run test",
"rebuild": "npm run clean && npm run build",
"test": "cross-env ../../node_modules/.bin/jest --selectProjects dockview",
"test:cov": "cross-env ../../node_modules/.bin/jest --selectProjects dockview --coverage"
},
"dependencies": {
"dockview-core": "^1.10.1"
}
}

View File

@ -0,0 +1,113 @@
/* eslint-disable */
const { join } = require('path');
const typescript = require('@rollup/plugin-typescript');
const terser = require('@rollup/plugin-terser');
const postcss = require('rollup-plugin-postcss');
const nodeResolve = require('@rollup/plugin-node-resolve');
const { name, version, homepage, license } = require('./package.json');
const main = join(__dirname, './scripts/rollupEntryTarget.ts');
const mainNoStyles = join(__dirname, './src/index.ts');
const outputDir = join(__dirname, 'dist');
function outputFile(format, isMinified, withStyles) {
let filename = join(outputDir, name);
if (format !== 'umd') {
filename += `.${format}`;
}
if (isMinified) {
filename += '.min';
}
if (!withStyles) {
filename += '.noStyle';
}
return `${filename}.js`;
}
function getInput(options) {
const { withStyles } = options;
if (withStyles) {
return main;
}
return mainNoStyles;
}
function createBundle(format, options) {
const { withStyles, isMinified } = options;
const input = getInput(options);
const file = outputFile(format, isMinified, withStyles);
const external = [];
const output = {
file,
format,
sourcemap: true,
globals: {},
banner: [
`/**`,
` * ${name}`,
` * @version ${version}`,
` * @link ${homepage}`,
` * @license ${license}`,
` */`,
].join('\n'),
};
const plugins = [
nodeResolve({
include: ['node_modules/dockview-core/**'],
}),
typescript({
tsconfig: 'tsconfig.esm.json',
}),
];
if (isMinified) {
plugins.push(terser());
}
if (withStyles) {
plugins.push(postcss());
}
if (format === 'umd') {
output['name'] = name;
}
external.push('react', 'react-dom');
if (format === 'umd') {
output.globals['react'] = 'React';
output.globals['react-dom'] = 'ReactDOM';
}
return {
input,
output,
plugins,
external,
};
}
module.exports = [
// amd
createBundle('amd', { withStyles: false, isMinified: false }),
createBundle('amd', { withStyles: true, isMinified: false }),
createBundle('amd', { withStyles: false, isMinified: true }),
createBundle('amd', { withStyles: true, isMinified: true }),
// umd
createBundle('umd', { withStyles: false, isMinified: false }),
createBundle('umd', { withStyles: true, isMinified: false }),
createBundle('umd', { withStyles: false, isMinified: true }),
createBundle('umd', { withStyles: true, isMinified: true }),
// cjs
createBundle('cjs', { withStyles: true, isMinified: false }),
// esm
createBundle('esm', { withStyles: true, isMinified: false }),
createBundle('esm', { withStyles: true, isMinified: true }),
];

View File

@ -0,0 +1,2 @@
import '../dist/styles/dockview.css';
export * from '../src/index';

View File

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

View File

@ -0,0 +1,121 @@
<script setup lang="ts">
import {
DockviewApi,
DockviewComponent,
type DockviewDndOverlayEvent,
type DockviewPanelRenderer,
type DroptargetOverlayModel,
type IContentRenderer,
type ITabRenderer,
type IWatermarkRenderer
} from 'dockview-core'
import { ref, onMounted, watch, onBeforeUnmount } from 'vue'
import {
VueContentRenderer,
VueTabRenderer,
VueWatermarkRenderer,
type ComponentInterface
} from './utils'
interface Props {
// onReady: (event: DockviewReadyEvent) => void;
components: Record<string, any>
tabComponents?: Record<string, any>
// watermarkComponent?: React.FunctionComponent<IWatermarkPanelProps>;
// onDidDrop?: (event: DockviewDidDropEvent) => void;
// onWillDrop?: (event: DockviewWillDropEvent) => void;
// showDndOverlay?: (event: DockviewDndOverlayEvent) => boolean;
hideBorders?: boolean;
className?: string;
disableAutoResizing?: boolean;
// defaultTabComponent?: React.FunctionComponent<IDockviewPanelHeaderProps>;
// rightHeaderActionsComponent?: React.FunctionComponent<IDockviewHeaderActionsProps>;
// leftHeaderActionsComponent?: React.FunctionComponent<IDockviewHeaderActionsProps>;
// prefixHeaderActionsComponent?: React.FunctionComponent<IDockviewHeaderActionsProps>;
singleTabMode?: 'fullwidth' | 'default';
disableFloatingGroups?: boolean;
floatingGroupBounds?:
| 'boundedWithinViewport'
| {
minimumHeightWithinViewport?: number;
minimumWidthWithinViewport?: number;
};
debug?: boolean;
defaultRenderer?: DockviewPanelRenderer;
rootOverlayModel?: DroptargetOverlayModel
locked?: boolean;
disableDnd?: boolean;
}
interface Emits {
(event: 'ready', value: { api: DockviewApi }): void
(event: 'showDndOverlay', value: DockviewDndOverlayEvent):void
}
const props = defineProps<Props>()
const emit = defineEmits<Emits>()
const el = ref<HTMLElement | null>(null)
const instance = ref<DockviewComponent | null>(null)
watch(() => props.components, (newValue, oldValue) => {
if (instance.value) {
instance.value.updateOptions({ components: newValue })
}
})
onMounted(() => {
if (!el.value) {
throw new Error('element is not mounted')
}
const dockview = new DockviewComponent({
parentElement: el.value,
frameworkComponentFactory: {
content: {
createComponent: (id: string, componentId: string, component: any): IContentRenderer => {
console.log('dockview-vue: createComponent')
return new VueContentRenderer(component as ComponentInterface)
}
},
tab: {
createComponent: (id: string, componentId: string, component: any): ITabRenderer => {
return new VueTabRenderer(component as ComponentInterface)
}
},
watermark: {
createComponent: (id: string, componentId: string, component: any): IWatermarkRenderer => {
return new VueWatermarkRenderer(component as ComponentInterface)
}
}
},
frameworkComponents: props.components,
disableAutoResizing: props.disableAutoResizing,
frameworkTabComponents: props.tabComponents,
singleTabMode: props.singleTabMode,
disableFloatingGroups: props.disableFloatingGroups,
floatingGroupBounds: props.floatingGroupBounds,
defaultRenderer: props.defaultRenderer,
debug: props.debug,
rootOverlayModel: props.rootOverlayModel,
locked: props.locked,
disableDnd: props.disableDnd,
})
instance.value = dockview
emit("ready", { api: new DockviewApi(dockview) })
})
onBeforeUnmount(() => {
if (instance.value) {
instance.value.dispose()
}
})
</script>
<template>
<div ref="el" />
</template>

View File

View File

@ -0,0 +1 @@
export * from 'dockview-core';

View File

@ -0,0 +1,151 @@
import type {
DockviewGroupPanel,
GroupPanelPartInitParameters,
IContentRenderer,
ITabRenderer,
IWatermarkRenderer,
PanelUpdateEvent,
Parameters,
WatermarkRendererInitParameters
} from 'dockview-core'
import { createVNode, type ComponentOptionsBase, render, cloneVNode, mergeProps } from 'vue'
export type ComponentInterface = ComponentOptionsBase<any, any, any, any, any, any, any, any>
/**
* TODO List
*
* 1. handle vue context-ish stuff (appContext? provides?)
*
*
*
* @see https://vuejs.org/api/render-function.html#clonevnode
* @see https://vuejs.org/api/render-function.html#mergeprops
*/
export function mountVueComponent(component: ComponentInterface, props: any, element: HTMLElement) {
let vNode = createVNode(component, props)
render(vNode, element)
return {
update: (newProps: any) => {
vNode = cloneVNode(vNode, mergeProps(props, newProps))
render(vNode, element)
},
dispose: () => {
render(null, element)
}
}
}
export class VueContentRenderer implements IContentRenderer {
private _element: HTMLElement
private _renderDisposable: { update: (props: any) => void; dispose: () => void } | undefined
get element(): HTMLElement {
return this._element
}
constructor(private readonly component: ComponentInterface) {
this._element = document.createElement('div')
this.element.className = 'dv-vue-part'
}
init(parameters: GroupPanelPartInitParameters): void {
const props = {
params: parameters.params,
api: parameters.api,
containerApi: parameters.containerApi
}
this._renderDisposable?.dispose()
this._renderDisposable = mountVueComponent(this.component, props, this.element)
}
update(event: PanelUpdateEvent<Parameters>): void {
const params = event.params
// TODO: handle prop updates somehow?
this._renderDisposable?.update(params)
}
focus(): void {
// TODO: make optional on interface
}
dispose(): void {
this._renderDisposable?.dispose()
}
}
export class VueTabRenderer implements ITabRenderer {
private _element: HTMLElement
private _renderDisposable: { update: (props: any) => void; dispose: () => void } | undefined
get element(): HTMLElement {
return this._element
}
constructor(private readonly component: ComponentInterface) {
this._element = document.createElement('div')
this.element.className = 'dv-vue-part'
}
init(parameters: GroupPanelPartInitParameters): void {
const props = {
params: parameters.params,
api: parameters.api,
containerApi: parameters.containerApi
}
this._renderDisposable?.dispose()
this._renderDisposable = mountVueComponent(this.component, props, this.element)
}
update(event: PanelUpdateEvent<Parameters>): void {
const params = event.params
// TODO: handle prop updates somehow?
this._renderDisposable?.update(params)
}
dispose(): void {
this._renderDisposable?.dispose()
}
}
export class VueWatermarkRenderer implements IWatermarkRenderer {
private _element: HTMLElement
private _renderDisposable: { update: (props: any) => void; dispose: () => void } | undefined
get element(): HTMLElement {
return this._element
}
constructor(private readonly component: ComponentInterface) {
this._element = document.createElement('div')
this.element.className = 'dv-vue-part'
}
init(parameters: WatermarkRendererInitParameters): void {
const props = {
group: parameters.group,
containerApi: parameters.containerApi
}
this._renderDisposable?.dispose()
this._renderDisposable = mountVueComponent(this.component, props, this.element)
}
updateParentGroup(group: DockviewGroupPanel, visible: boolean): void {
// TODO: make optional on interface
}
update(event: PanelUpdateEvent<Parameters>): void {
const params = event.params
// TODO: handle prop updates somehow?
this._renderDisposable?.update(params)
}
dispose(): void {
this._renderDisposable?.dispose()
}
}

View File

@ -0,0 +1,14 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"module": "ES2020",
"moduleResolution": "node",
"target": "es6",
"outDir": "dist/esm",
"tsBuildInfoFile": ".build/tsconfig.tsbuildinfo.esm",
"jsx": "react",
"rootDir": "src"
},
"include": ["src"],
"exclude": ["**/node_modules", "src/__tests__"]
}

View File

@ -0,0 +1,11 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"outDir": "dist/cjs",
"tsBuildInfoFile": ".build/tsconfig.tsbuildinfo.cjs",
"jsx": "react",
"rootDir": "src"
},
"include": ["src"],
"exclude": ["**/node_modules", "src/__tests__"]
}

View File

@ -0,0 +1,5 @@
{
"extends": ["../../typedoc.base.json"],
"entryPoints": ["src/index.ts"],
"exclude": ["**/dist/**"]
}

View File

@ -2,7 +2,7 @@ import { fireEvent } from '@testing-library/dom';
import { Emitter, Event } from '../../../../events'; import { Emitter, Event } from '../../../../events';
import { ContentContainer } from '../../../../dockview/components/panel/content'; import { ContentContainer } from '../../../../dockview/components/panel/content';
import { import {
GroupPanelContentPartInitParameters, GroupPanelPartInitParameters,
IContentRenderer, IContentRenderer,
} from '../../../../dockview/types'; } from '../../../../dockview/types';
import { CompositeDisposable } from '../../../../lifecycle'; import { CompositeDisposable } from '../../../../lifecycle';
@ -20,18 +20,13 @@ class TestContentRenderer
{ {
readonly element: HTMLElement; readonly element: HTMLElement;
readonly _onDidFocus = new Emitter<void>();
readonly _onDidBlur = new Emitter<void>();
readonly onDidFocus: Event<void> = this._onDidFocus.event;
readonly onDidBlur: Event<void> = this._onDidBlur.event;
constructor(public id: string) { constructor(public id: string) {
super(); super();
this.element = document.createElement('div'); this.element = document.createElement('div');
this.element.id = id; this.element.id = id;
} }
init(parameters: GroupPanelContentPartInitParameters): void { init(parameters: GroupPanelPartInitParameters): void {
// //
} }
@ -110,16 +105,6 @@ describe('contentContainer', () => {
expect(focus).toBe(1); expect(focus).toBe(1);
expect(blur).toBe(1); expect(blur).toBe(1);
// renderer explicitly asks for focus
contentRenderer._onDidFocus.fire();
expect(focus).toBe(2);
expect(blur).toBe(1);
// renderer explicitly looses focus
contentRenderer._onDidBlur.fire();
expect(focus).toBe(2);
expect(blur).toBe(2);
const contentRenderer2 = new TestContentRenderer('id-2'); const contentRenderer2 = new TestContentRenderer('id-2');
const panel2 = { const panel2 = {
@ -130,25 +115,19 @@ describe('contentContainer', () => {
} as Partial<IDockviewPanel>; } as Partial<IDockviewPanel>;
cut.openPanel(panel2 as IDockviewPanel); cut.openPanel(panel2 as IDockviewPanel);
expect(focus).toBe(2); // expect(focus).toBe(2);
expect(blur).toBe(2); // expect(blur).toBe(1);
// previous renderer events should no longer be attached to container
contentRenderer._onDidFocus.fire();
contentRenderer._onDidBlur.fire();
expect(focus).toBe(2);
expect(blur).toBe(2);
// new panel recieves focus // new panel recieves focus
fireEvent.focus(contentRenderer2.element); fireEvent.focus(contentRenderer2.element);
expect(focus).toBe(3); expect(focus).toBe(2);
expect(blur).toBe(2); expect(blur).toBe(1);
// new panel looses focus // new panel looses focus
fireEvent.blur(contentRenderer2.element); fireEvent.blur(contentRenderer2.element);
jest.runAllTimers(); jest.runAllTimers();
expect(focus).toBe(3); expect(focus).toBe(2);
expect(blur).toBe(3); expect(blur).toBe(2);
disposable.dispose(); disposable.dispose();
}); });

View File

@ -18,6 +18,7 @@ import {
} from '../../dockview/components/titlebar/tabsContainer'; } from '../../dockview/components/titlebar/tabsContainer';
import { fromPartial } from '@total-typescript/shoehorn'; import { fromPartial } from '@total-typescript/shoehorn';
import { DockviewApi } from '../../api/component.api'; import { DockviewApi } from '../../api/component.api';
import { DockviewDndOverlayEvent } from '../../dockview/options';
class PanelContentPartTest implements IContentRenderer { class PanelContentPartTest implements IContentRenderer {
element: HTMLElement = document.createElement('div'); element: HTMLElement = document.createElement('div');
@ -2040,7 +2041,6 @@ describe('dockviewComponent', () => {
tabComponents: { tabComponents: {
test_tab_id: PanelTabPartTest, test_tab_id: PanelTabPartTest,
}, },
orientation: Orientation.HORIZONTAL,
}); });
dockview.layout(1000, 1000); dockview.layout(1000, 1000);
@ -2147,7 +2147,6 @@ describe('dockviewComponent', () => {
tabComponents: { tabComponents: {
test_tab_id: PanelTabPartTest, test_tab_id: PanelTabPartTest,
}, },
orientation: Orientation.HORIZONTAL,
}); });
dockview.layout(1000, 1000); dockview.layout(1000, 1000);
@ -2289,7 +2288,6 @@ describe('dockviewComponent', () => {
tabComponents: { tabComponents: {
test_tab_id: PanelTabPartTest, test_tab_id: PanelTabPartTest,
}, },
orientation: Orientation.HORIZONTAL,
}); });
dockview.layout(1000, 1000); dockview.layout(1000, 1000);
@ -2418,7 +2416,6 @@ describe('dockviewComponent', () => {
tabComponents: { tabComponents: {
test_tab_id: PanelTabPartTest, test_tab_id: PanelTabPartTest,
}, },
orientation: Orientation.HORIZONTAL,
}); });
dockview.layout(1000, 1000); dockview.layout(1000, 1000);
@ -2467,66 +2464,6 @@ describe('dockviewComponent', () => {
}); });
}); });
test('orthogonal realigment #5', () => {
const container = document.createElement('div');
const dockview = new DockviewComponent({
parentElement: container,
components: {
default: PanelContentPartTest,
},
tabComponents: {
test_tab_id: PanelTabPartTest,
},
orientation: Orientation.VERTICAL,
});
dockview.layout(1000, 1000);
expect(dockview.orientation).toBe(Orientation.VERTICAL);
dockview.addPanel({
id: 'panel1',
component: 'default',
position: {
direction: 'left',
},
});
expect(dockview.orientation).toBe(Orientation.HORIZONTAL);
expect(JSON.parse(JSON.stringify(dockview.toJSON()))).toEqual({
activeGroup: '1',
grid: {
root: {
type: 'branch',
data: [
{
type: 'leaf',
data: {
views: ['panel1'],
id: '1',
activeView: 'panel1',
},
size: 1000,
},
],
size: 1000,
},
height: 1000,
width: 1000,
orientation: Orientation.HORIZONTAL,
},
panels: {
panel1: {
id: 'panel1',
contentComponent: 'default',
title: 'panel1',
},
},
});
});
test('that a empty component has no groups', () => { test('that a empty component has no groups', () => {
const container = document.createElement('div'); const container = document.createElement('div');
@ -2538,7 +2475,6 @@ describe('dockviewComponent', () => {
tabComponents: { tabComponents: {
test_tab_id: PanelTabPartTest, test_tab_id: PanelTabPartTest,
}, },
orientation: Orientation.HORIZONTAL,
}); });
expect(dockview.groups.length).toBe(0); expect(dockview.groups.length).toBe(0);
@ -2555,7 +2491,6 @@ describe('dockviewComponent', () => {
tabComponents: { tabComponents: {
test_tab_id: PanelTabPartTest, test_tab_id: PanelTabPartTest,
}, },
orientation: Orientation.HORIZONTAL,
}); });
expect(dockview.groups.length).toBe(0); expect(dockview.groups.length).toBe(0);
@ -2595,7 +2530,6 @@ describe('dockviewComponent', () => {
tabComponents: { tabComponents: {
test_tab_id: PanelTabPartTest, test_tab_id: PanelTabPartTest,
}, },
orientation: Orientation.HORIZONTAL,
}); });
expect(JSON.parse(JSON.stringify(dockview.toJSON()))).toEqual({ expect(JSON.parse(JSON.stringify(dockview.toJSON()))).toEqual({
@ -2624,7 +2558,6 @@ describe('dockviewComponent', () => {
tabComponents: { tabComponents: {
test_tab_id: PanelTabPartTest, test_tab_id: PanelTabPartTest,
}, },
orientation: Orientation.HORIZONTAL,
}); });
dockview.layout(100, 100); dockview.layout(100, 100);
@ -2711,7 +2644,6 @@ describe('dockviewComponent', () => {
tabComponents: { tabComponents: {
test_tab_id: PanelTabPartTest, test_tab_id: PanelTabPartTest,
}, },
orientation: Orientation.HORIZONTAL,
}); });
dockview.layout(100, 100); dockview.layout(100, 100);
@ -2774,7 +2706,6 @@ describe('dockviewComponent', () => {
tabComponents: { tabComponents: {
test_tab_id: PanelTabPartTest, test_tab_id: PanelTabPartTest,
}, },
orientation: Orientation.HORIZONTAL,
}); });
expect(dockview.orientation).toBe(Orientation.HORIZONTAL); expect(dockview.orientation).toBe(Orientation.HORIZONTAL);
@ -2865,7 +2796,6 @@ describe('dockviewComponent', () => {
tabComponents: { tabComponents: {
test_tab_id: PanelTabPartTest, test_tab_id: PanelTabPartTest,
}, },
orientation: Orientation.HORIZONTAL,
}); });
dockview.layout(1000, 500); dockview.layout(1000, 500);
@ -2897,7 +2827,6 @@ describe('dockviewComponent', () => {
tabComponents: { tabComponents: {
test_tab_id: PanelTabPartTest, test_tab_id: PanelTabPartTest,
}, },
orientation: Orientation.HORIZONTAL,
}); });
dockview.layout(1000, 500); dockview.layout(1000, 500);
@ -2922,8 +2851,6 @@ describe('dockviewComponent', () => {
test('that external dnd events do not trigger the top-level center dnd target unless empty', () => { test('that external dnd events do not trigger the top-level center dnd target unless empty', () => {
const container = document.createElement('div'); const container = document.createElement('div');
const showDndOverlay = jest.fn().mockReturnValue(true);
const dockview = new DockviewComponent({ const dockview = new DockviewComponent({
parentElement: container, parentElement: container,
components: { components: {
@ -2932,8 +2859,13 @@ describe('dockviewComponent', () => {
tabComponents: { tabComponents: {
test_tab_id: PanelTabPartTest, test_tab_id: PanelTabPartTest,
}, },
orientation: Orientation.HORIZONTAL, });
showDndOverlay: showDndOverlay,
let events: DockviewDndOverlayEvent[] = [];
dockview.onUnhandledDragOverEvent((e) => {
events.push(e);
e.accept();
}); });
dockview.layout(1000, 500); dockview.layout(1000, 500);
@ -2973,13 +2905,11 @@ describe('dockviewComponent', () => {
}); });
fireEvent(dockview.element, eventLeft); fireEvent(dockview.element, eventLeft);
expect(showDndOverlay).toHaveBeenCalledWith({ expect(events[0].nativeEvent).toBe(eventLeft);
nativeEvent: eventLeft, expect(events[0].position).toBe('left');
position: 'left', expect(events[0].target).toBe('edge');
target: 'edge', expect(events[0].getData).toBe(getPanelData);
getData: getPanelData, expect(events.length).toBe(1);
});
expect(showDndOverlay).toBeCalledTimes(1);
// right // right
@ -2992,13 +2922,11 @@ describe('dockviewComponent', () => {
}); });
fireEvent(dockview.element, eventRight); fireEvent(dockview.element, eventRight);
expect(showDndOverlay).toHaveBeenCalledWith({ expect(events[1].nativeEvent).toBe(eventRight);
nativeEvent: eventRight, expect(events[1].position).toBe('right');
position: 'right', expect(events[1].target).toBe('edge');
target: 'edge', expect(events[1].getData).toBe(getPanelData);
getData: getPanelData, expect(events.length).toBe(2);
});
expect(showDndOverlay).toBeCalledTimes(2);
// top // top
@ -3011,13 +2939,11 @@ describe('dockviewComponent', () => {
}); });
fireEvent(dockview.element, eventTop); fireEvent(dockview.element, eventTop);
expect(showDndOverlay).toHaveBeenCalledWith({ expect(events[2].nativeEvent).toBe(eventTop);
nativeEvent: eventTop, expect(events[2].position).toBe('top');
position: 'top', expect(events[2].target).toBe('edge');
target: 'edge', expect(events[2].getData).toBe(getPanelData);
getData: getPanelData, expect(events.length).toBe(3);
});
expect(showDndOverlay).toBeCalledTimes(3);
// top // top
@ -3030,13 +2956,11 @@ describe('dockviewComponent', () => {
}); });
fireEvent(dockview.element, eventBottom); fireEvent(dockview.element, eventBottom);
expect(showDndOverlay).toHaveBeenCalledWith({ expect(events[3].nativeEvent).toBe(eventBottom);
nativeEvent: eventBottom, expect(events[3].position).toBe('bottom');
position: 'bottom', expect(events[3].target).toBe('edge');
target: 'edge', expect(events[3].getData).toBe(getPanelData);
getData: getPanelData, expect(events.length).toBe(4);
});
expect(showDndOverlay).toBeCalledTimes(4);
// center // center
@ -3050,7 +2974,7 @@ describe('dockviewComponent', () => {
fireEvent(dockview.element, eventCenter); fireEvent(dockview.element, eventCenter);
// expect not to be called for center // expect not to be called for center
expect(showDndOverlay).toBeCalledTimes(4); expect(events.length).toBe(4);
dockview.removePanel(panel1); dockview.removePanel(panel1);
dockview.removePanel(panel2); dockview.removePanel(panel2);
@ -3066,13 +2990,11 @@ describe('dockviewComponent', () => {
}); });
fireEvent(dockview.element, eventCenter2); fireEvent(dockview.element, eventCenter2);
expect(showDndOverlay).toHaveBeenCalledWith({ expect(events[4].nativeEvent).toBe(eventCenter2);
nativeEvent: eventTop, expect(events[4].position).toBe('center');
position: 'center', expect(events[4].target).toBe('edge');
target: 'edge', expect(events[4].getData).toBe(getPanelData);
getData: getPanelData, expect(events.length).toBe(5);
});
expect(showDndOverlay).toBeCalledTimes(5);
}); });
test('that dragging a tab triggers onWillDragPanel', () => { test('that dragging a tab triggers onWillDragPanel', () => {
@ -3086,7 +3008,6 @@ describe('dockviewComponent', () => {
tabComponents: { tabComponents: {
test_tab_id: PanelTabPartTest, test_tab_id: PanelTabPartTest,
}, },
orientation: Orientation.HORIZONTAL,
}); });
dockview.layout(1000, 500); dockview.layout(1000, 500);
@ -3126,7 +3047,6 @@ describe('dockviewComponent', () => {
tabComponents: { tabComponents: {
test_tab_id: PanelTabPartTest, test_tab_id: PanelTabPartTest,
}, },
orientation: Orientation.HORIZONTAL,
}); });
dockview.layout(1000, 500); dockview.layout(1000, 500);
@ -3167,7 +3087,6 @@ describe('dockviewComponent', () => {
tabComponents: { tabComponents: {
test_tab_id: PanelTabPartTest, test_tab_id: PanelTabPartTest,
}, },
orientation: Orientation.HORIZONTAL,
}); });
dockview.layout(1000, 500); dockview.layout(1000, 500);
@ -3257,7 +3176,6 @@ describe('dockviewComponent', () => {
tabComponents: { tabComponents: {
test_tab_id: PanelTabPartTest, test_tab_id: PanelTabPartTest,
}, },
orientation: Orientation.HORIZONTAL,
}); });
dockview.layout(1000, 500); dockview.layout(1000, 500);
@ -3380,7 +3298,6 @@ describe('dockviewComponent', () => {
tabComponents: { tabComponents: {
test_tab_id: PanelTabPartTest, test_tab_id: PanelTabPartTest,
}, },
orientation: Orientation.HORIZONTAL,
}); });
expect(dockview.disableResizing).toBeFalsy(); expect(dockview.disableResizing).toBeFalsy();
@ -3398,7 +3315,6 @@ describe('dockviewComponent', () => {
tabComponents: { tabComponents: {
test_tab_id: PanelTabPartTest, test_tab_id: PanelTabPartTest,
}, },
orientation: Orientation.HORIZONTAL,
disableAutoResizing: true, disableAutoResizing: true,
}); });
@ -3417,7 +3333,6 @@ describe('dockviewComponent', () => {
tabComponents: { tabComponents: {
test_tab_id: PanelTabPartTest, test_tab_id: PanelTabPartTest,
}, },
orientation: Orientation.HORIZONTAL,
}); });
dockview.layout(1000, 500); dockview.layout(1000, 500);
@ -3445,7 +3360,6 @@ describe('dockviewComponent', () => {
tabComponents: { tabComponents: {
test_tab_id: PanelTabPartTest, test_tab_id: PanelTabPartTest,
}, },
orientation: Orientation.HORIZONTAL,
}); });
dockview.layout(1000, 500); dockview.layout(1000, 500);
@ -3488,7 +3402,6 @@ describe('dockviewComponent', () => {
tabComponents: { tabComponents: {
test_tab_id: PanelTabPartTest, test_tab_id: PanelTabPartTest,
}, },
orientation: Orientation.HORIZONTAL,
}); });
dockview.layout(1000, 500); dockview.layout(1000, 500);
@ -3531,7 +3444,6 @@ describe('dockviewComponent', () => {
tabComponents: { tabComponents: {
test_tab_id: PanelTabPartTest, test_tab_id: PanelTabPartTest,
}, },
orientation: Orientation.HORIZONTAL,
}); });
dockview.layout(1000, 500); dockview.layout(1000, 500);
@ -3582,7 +3494,6 @@ describe('dockviewComponent', () => {
tabComponents: { tabComponents: {
test_tab_id: PanelTabPartTest, test_tab_id: PanelTabPartTest,
}, },
orientation: Orientation.HORIZONTAL,
}); });
dockview.layout(1000, 500); dockview.layout(1000, 500);
@ -3633,7 +3544,6 @@ describe('dockviewComponent', () => {
tabComponents: { tabComponents: {
test_tab_id: PanelTabPartTest, test_tab_id: PanelTabPartTest,
}, },
orientation: Orientation.HORIZONTAL,
}); });
dockview.layout(1000, 500); dockview.layout(1000, 500);
@ -3684,7 +3594,6 @@ describe('dockviewComponent', () => {
tabComponents: { tabComponents: {
test_tab_id: PanelTabPartTest, test_tab_id: PanelTabPartTest,
}, },
orientation: Orientation.HORIZONTAL,
}); });
dockview.layout(1000, 500); dockview.layout(1000, 500);
@ -3743,7 +3652,6 @@ describe('dockviewComponent', () => {
tabComponents: { tabComponents: {
test_tab_id: PanelTabPartTest, test_tab_id: PanelTabPartTest,
}, },
orientation: Orientation.HORIZONTAL,
}); });
dockview.layout(1000, 500); dockview.layout(1000, 500);
@ -3786,7 +3694,6 @@ describe('dockviewComponent', () => {
tabComponents: { tabComponents: {
test_tab_id: PanelTabPartTest, test_tab_id: PanelTabPartTest,
}, },
orientation: Orientation.HORIZONTAL,
}); });
dockview.layout(1000, 500); dockview.layout(1000, 500);
@ -3829,7 +3736,6 @@ describe('dockviewComponent', () => {
tabComponents: { tabComponents: {
test_tab_id: PanelTabPartTest, test_tab_id: PanelTabPartTest,
}, },
orientation: Orientation.HORIZONTAL,
}); });
dockview.layout(1000, 500); dockview.layout(1000, 500);
@ -3880,7 +3786,6 @@ describe('dockviewComponent', () => {
tabComponents: { tabComponents: {
test_tab_id: PanelTabPartTest, test_tab_id: PanelTabPartTest,
}, },
orientation: Orientation.HORIZONTAL,
}); });
dockview.layout(1000, 500); dockview.layout(1000, 500);
@ -3931,7 +3836,6 @@ describe('dockviewComponent', () => {
tabComponents: { tabComponents: {
test_tab_id: PanelTabPartTest, test_tab_id: PanelTabPartTest,
}, },
orientation: Orientation.HORIZONTAL,
}); });
dockview.layout(1000, 500); dockview.layout(1000, 500);
@ -3982,7 +3886,6 @@ describe('dockviewComponent', () => {
tabComponents: { tabComponents: {
test_tab_id: PanelTabPartTest, test_tab_id: PanelTabPartTest,
}, },
orientation: Orientation.HORIZONTAL,
}); });
dockview.layout(1000, 500); dockview.layout(1000, 500);
@ -4041,7 +3944,6 @@ describe('dockviewComponent', () => {
tabComponents: { tabComponents: {
test_tab_id: PanelTabPartTest, test_tab_id: PanelTabPartTest,
}, },
orientation: Orientation.HORIZONTAL,
}); });
dockview.layout(1000, 500); dockview.layout(1000, 500);
@ -4092,7 +3994,6 @@ describe('dockviewComponent', () => {
tabComponents: { tabComponents: {
test_tab_id: PanelTabPartTest, test_tab_id: PanelTabPartTest,
}, },
orientation: Orientation.HORIZONTAL,
}); });
dockview.layout(1000, 500); dockview.layout(1000, 500);
@ -4142,7 +4043,6 @@ describe('dockviewComponent', () => {
tabComponents: { tabComponents: {
test_tab_id: PanelTabPartTest, test_tab_id: PanelTabPartTest,
}, },
orientation: Orientation.HORIZONTAL,
}); });
dockview.layout(1000, 500); dockview.layout(1000, 500);
@ -4193,7 +4093,6 @@ describe('dockviewComponent', () => {
tabComponents: { tabComponents: {
test_tab_id: PanelTabPartTest, test_tab_id: PanelTabPartTest,
}, },
orientation: Orientation.HORIZONTAL,
}); });
dockview.layout(1000, 500); dockview.layout(1000, 500);
@ -4243,7 +4142,6 @@ describe('dockviewComponent', () => {
tabComponents: { tabComponents: {
test_tab_id: PanelTabPartTest, test_tab_id: PanelTabPartTest,
}, },
orientation: Orientation.HORIZONTAL,
}); });
dockview.layout(1000, 500); dockview.layout(1000, 500);
@ -4283,7 +4181,6 @@ describe('dockviewComponent', () => {
tabComponents: { tabComponents: {
test_tab_id: PanelTabPartTest, test_tab_id: PanelTabPartTest,
}, },
orientation: Orientation.HORIZONTAL,
}); });
dockview.layout(1000, 500); dockview.layout(1000, 500);
@ -4322,7 +4219,6 @@ describe('dockviewComponent', () => {
tabComponents: { tabComponents: {
test_tab_id: PanelTabPartTest, test_tab_id: PanelTabPartTest,
}, },
orientation: Orientation.HORIZONTAL,
}); });
dockview.layout(1000, 500); dockview.layout(1000, 500);
@ -4362,7 +4258,6 @@ describe('dockviewComponent', () => {
tabComponents: { tabComponents: {
test_tab_id: PanelTabPartTest, test_tab_id: PanelTabPartTest,
}, },
orientation: Orientation.HORIZONTAL,
}); });
dockview.layout(1000, 500); dockview.layout(1000, 500);
@ -4423,7 +4318,6 @@ describe('dockviewComponent', () => {
tabComponents: { tabComponents: {
test_tab_id: PanelTabPartTest, test_tab_id: PanelTabPartTest,
}, },
orientation: Orientation.HORIZONTAL,
}); });
dockview.layout(1000, 500); dockview.layout(1000, 500);
@ -4462,7 +4356,6 @@ describe('dockviewComponent', () => {
tabComponents: { tabComponents: {
test_tab_id: PanelTabPartTest, test_tab_id: PanelTabPartTest,
}, },
orientation: Orientation.HORIZONTAL,
}); });
dockview.layout(1000, 500); dockview.layout(1000, 500);
@ -4495,7 +4388,6 @@ describe('dockviewComponent', () => {
tabComponents: { tabComponents: {
test_tab_id: PanelTabPartTest, test_tab_id: PanelTabPartTest,
}, },
orientation: Orientation.HORIZONTAL,
}); });
dockview.layout(1000, 500); dockview.layout(1000, 500);
@ -4568,7 +4460,6 @@ describe('dockviewComponent', () => {
tabComponents: { tabComponents: {
test_tab_id: PanelTabPartTest, test_tab_id: PanelTabPartTest,
}, },
orientation: Orientation.HORIZONTAL,
}); });
dockview.layout(1000, 500); dockview.layout(1000, 500);
@ -4609,7 +4500,6 @@ describe('dockviewComponent', () => {
tabComponents: { tabComponents: {
test_tab_id: PanelTabPartTest, test_tab_id: PanelTabPartTest,
}, },
orientation: Orientation.HORIZONTAL,
}); });
const api = new DockviewApi(dockview); const api = new DockviewApi(dockview);

View File

@ -642,7 +642,6 @@ describe('dockviewGroupPanelModel', () => {
return { return {
id: 'testcomponentid', id: 'testcomponentid',
options: { options: {
showDndOverlay: jest.fn(),
parentElement: document.createElement('div'), parentElement: document.createElement('div'),
}, },
getPanel: jest.fn(), getPanel: jest.fn(),
@ -650,6 +649,7 @@ describe('dockviewGroupPanelModel', () => {
onDidRemovePanel: jest.fn(), onDidRemovePanel: jest.fn(),
}; };
}); });
const accessor = new accessorMock() as DockviewComponent; const accessor = new accessorMock() as DockviewComponent;
const groupviewMock = jest.fn<Partial<DockviewGroupPanelModel>, []>( const groupviewMock = jest.fn<Partial<DockviewGroupPanelModel>, []>(
() => { () => {
@ -679,6 +679,12 @@ describe('dockviewGroupPanelModel', () => {
new groupPanelMock() as DockviewGroupPanel new groupPanelMock() as DockviewGroupPanel
); );
let counter = 0;
cut.onUnhandledDragOverEvent(() => {
counter++;
});
const element = container const element = container
.getElementsByClassName('content-container') .getElementsByClassName('content-container')
.item(0)!; .item(0)!;
@ -691,7 +697,7 @@ describe('dockviewGroupPanelModel', () => {
fireEvent.dragEnter(element); fireEvent.dragEnter(element);
fireEvent.dragOver(element); fireEvent.dragOver(element);
expect(accessor.options.showDndOverlay).toBeCalledTimes(1); expect(counter).toBe(1);
expect( expect(
element.getElementsByClassName('drop-target-dropzone').length element.getElementsByClassName('drop-target-dropzone').length
@ -703,7 +709,6 @@ describe('dockviewGroupPanelModel', () => {
return { return {
id: 'testcomponentid', id: 'testcomponentid',
options: { options: {
showDndOverlay: () => true,
parentElement: document.createElement('div'), parentElement: document.createElement('div'),
}, },
getPanel: jest.fn(), getPanel: jest.fn(),
@ -740,6 +745,10 @@ describe('dockviewGroupPanelModel', () => {
new groupPanelMock() as DockviewGroupPanel new groupPanelMock() as DockviewGroupPanel
); );
cut.onUnhandledDragOverEvent((e) => {
e.accept();
});
const element = container const element = container
.getElementsByClassName('content-container') .getElementsByClassName('content-container')
.item(0)!; .item(0)!;
@ -795,7 +804,6 @@ describe('dockviewGroupPanelModel', () => {
return { return {
id: 'testcomponentid', id: 'testcomponentid',
options: { options: {
showDndOverlay: jest.fn(),
parentElement: document.createElement('div'), parentElement: document.createElement('div'),
}, },
getPanel: jest.fn(), getPanel: jest.fn(),
@ -834,6 +842,12 @@ describe('dockviewGroupPanelModel', () => {
new groupPanelMock() as DockviewGroupPanel new groupPanelMock() as DockviewGroupPanel
); );
let counter = 0;
cut.onUnhandledDragOverEvent(() => {
counter++;
});
cut.openPanel( cut.openPanel(
new TestPanel('panel1', { new TestPanel('panel1', {
renderer: 'onlyWhenVisibile', renderer: 'onlyWhenVisibile',
@ -857,7 +871,7 @@ describe('dockviewGroupPanelModel', () => {
fireEvent.dragEnter(element); fireEvent.dragEnter(element);
fireEvent.dragOver(element); fireEvent.dragOver(element);
expect(accessor.options.showDndOverlay).toBeCalledTimes(0); expect(counter).toBe(0);
expect( expect(
element.getElementsByClassName('drop-target-dropzone').length element.getElementsByClassName('drop-target-dropzone').length
@ -869,7 +883,6 @@ describe('dockviewGroupPanelModel', () => {
return { return {
id: 'testcomponentid', id: 'testcomponentid',
options: { options: {
showDndOverlay: jest.fn(),
parentElement: document.createElement('div'), parentElement: document.createElement('div'),
}, },
getPanel: jest.fn(), getPanel: jest.fn(),
@ -908,6 +921,12 @@ describe('dockviewGroupPanelModel', () => {
new groupPanelMock() as DockviewGroupPanel new groupPanelMock() as DockviewGroupPanel
); );
let counter = 0;
cut.onUnhandledDragOverEvent(() => {
counter++;
});
cut.openPanel( cut.openPanel(
new TestPanel('panel1', { new TestPanel('panel1', {
renderer: 'onlyWhenVisibile', renderer: 'onlyWhenVisibile',
@ -936,7 +955,7 @@ describe('dockviewGroupPanelModel', () => {
fireEvent.dragEnter(element); fireEvent.dragEnter(element);
fireEvent.dragOver(element); fireEvent.dragOver(element);
expect(accessor.options.showDndOverlay).toBeCalledTimes(0); expect(counter).toBe(0);
expect( expect(
element.getElementsByClassName('drop-target-dropzone').length element.getElementsByClassName('drop-target-dropzone').length
@ -948,7 +967,6 @@ describe('dockviewGroupPanelModel', () => {
return { return {
id: 'testcomponentid', id: 'testcomponentid',
options: { options: {
showDndOverlay: jest.fn(),
parentElement: document.createElement('div'), parentElement: document.createElement('div'),
}, },
getPanel: jest.fn(), getPanel: jest.fn(),
@ -987,6 +1005,12 @@ describe('dockviewGroupPanelModel', () => {
new groupPanelMock() as DockviewGroupPanel new groupPanelMock() as DockviewGroupPanel
); );
let counter = 0;
cut.onUnhandledDragOverEvent(() => {
counter++;
});
cut.openPanel( cut.openPanel(
new TestPanel('panel1', { new TestPanel('panel1', {
renderer: 'onlyWhenVisibile', renderer: 'onlyWhenVisibile',
@ -1015,7 +1039,7 @@ describe('dockviewGroupPanelModel', () => {
fireEvent.dragEnter(element); fireEvent.dragEnter(element);
fireEvent.dragOver(element); fireEvent.dragOver(element);
expect(accessor.options.showDndOverlay).toBeCalledTimes(1); expect(counter).toBe(1);
expect( expect(
element.getElementsByClassName('drop-target-dropzone').length element.getElementsByClassName('drop-target-dropzone').length

View File

@ -5,6 +5,7 @@ import {
import { import {
AddGroupOptions, AddGroupOptions,
AddPanelOptions, AddPanelOptions,
DockviewDndOverlayEvent,
MovementOptions, MovementOptions,
} from '../dockview/options'; } from '../dockview/options';
import { Parameters } from '../panel/types'; import { Parameters } from '../panel/types';
@ -695,6 +696,10 @@ export class DockviewApi implements CommonApi<SerializedDockview> {
return this.component.onWillDragPanel; return this.component.onWillDragPanel;
} }
get onUnhandledDragOverEvent(): Event<DockviewDndOverlayEvent> {
return this.component.onUnhandledDragOverEvent;
}
/** /**
* All panel objects. * All panel objects.
*/ */

View File

@ -68,9 +68,10 @@ export function positionToDirection(position: Position): Direction {
export type Position = 'top' | 'bottom' | 'left' | 'right' | 'center'; export type Position = 'top' | 'bottom' | 'left' | 'right' | 'center';
export type CanDisplayOverlay = export type CanDisplayOverlay = (
| boolean dragEvent: DragEvent,
| ((dragEvent: DragEvent, state: Position) => boolean); state: Position
) => boolean;
export type MeasuredValue = { value: number; type: 'pixels' | 'percentage' }; export type MeasuredValue = { value: number; type: 'pixels' | 'percentage' };
@ -170,6 +171,11 @@ export class Droptarget extends CompositeDisposable {
return; return;
} }
if (!this.options.canDisplayOverlay(e, quadrant)) {
this.removeDropTarget();
return;
}
const willShowOverlayEvent = new WillShowOverlayEvent({ const willShowOverlayEvent = new WillShowOverlayEvent({
nativeEvent: e, nativeEvent: e,
position: quadrant, position: quadrant,
@ -186,16 +192,6 @@ export class Droptarget extends CompositeDisposable {
return; return;
} }
if (typeof this.options.canDisplayOverlay === 'boolean') {
if (!this.options.canDisplayOverlay) {
this.removeDropTarget();
return;
}
} else if (!this.options.canDisplayOverlay(e, quadrant)) {
this.removeDropTarget();
return;
}
this.markAsUsed(e); this.markAsUsed(e);
if (!this.targetElement) { if (!this.targetElement) {

View File

@ -157,9 +157,6 @@ export class ContentContainer
} }
if (doRender) { if (doRender) {
const _onDidFocus = panel.view.content.onDidFocus;
const _onDidBlur = panel.view.content.onDidBlur;
const focusTracker = trackFocus(container); const focusTracker = trackFocus(container);
const disposable = new CompositeDisposable(); const disposable = new CompositeDisposable();
@ -169,17 +166,6 @@ export class ContentContainer
focusTracker.onDidBlur(() => this._onDidBlur.fire()) focusTracker.onDidBlur(() => this._onDidBlur.fire())
); );
if (_onDidFocus) {
disposable.addDisposables(
_onDidFocus(() => this._onDidFocus.fire())
);
}
if (_onDidBlur) {
disposable.addDisposables(
_onDidBlur(() => this._onDidBlur.fire())
);
}
this.disposable.value = disposable; this.disposable.value = disposable;
} }
} }

View File

@ -12,7 +12,7 @@ import {
} from '../dnd/droptarget'; } from '../dnd/droptarget';
import { tail, sequenceEquals, remove } from '../array'; import { tail, sequenceEquals, remove } from '../array';
import { DockviewPanel, IDockviewPanel } from './dockviewPanel'; import { DockviewPanel, IDockviewPanel } from './dockviewPanel';
import { CompositeDisposable, Disposable, IDisposable } from '../lifecycle'; import { CompositeDisposable, Disposable } from '../lifecycle';
import { Event, Emitter, addDisposableWindowListener } from '../events'; import { Event, Emitter, addDisposableWindowListener } from '../events';
import { Watermark } from './components/watermark/watermark'; import { Watermark } from './components/watermark/watermark';
import { IWatermarkRenderer, GroupviewPanelState } from './types'; import { IWatermarkRenderer, GroupviewPanelState } from './types';
@ -23,6 +23,9 @@ import {
AddGroupOptions, AddGroupOptions,
AddPanelOptions, AddPanelOptions,
DockviewComponentOptions, DockviewComponentOptions,
DockviewDndOverlayEvent,
DockviewOptions,
DockviewUnhandledDragOverEvent,
isGroupOptionsWithGroup, isGroupOptionsWithGroup,
isGroupOptionsWithPanel, isGroupOptionsWithPanel,
isPanelOptionsWithGroup, isPanelOptionsWithGroup,
@ -232,25 +235,6 @@ function typeValidate(data: SerializedDockview): void {
typeValidate2(grid.root, '.grid.root'); typeValidate2(grid.root, '.grid.root');
} }
export type DockviewComponentUpdateOptions = Pick<
DockviewComponentOptions,
| 'orientation'
| 'components'
| 'frameworkComponents'
| 'tabComponents'
| 'frameworkTabComponents'
| 'showDndOverlay'
| 'watermarkFrameworkComponent'
| 'defaultTabComponent'
| 'createLeftHeaderActionsElement'
| 'createRightHeaderActionsElement'
| 'createPrefixHeaderActionsElement'
| 'disableFloatingGroups'
| 'floatingGroupBounds'
| 'rootOverlayModel'
| 'disableDnd'
>;
type MoveGroupOptions = { type MoveGroupOptions = {
from: { group: DockviewGroupPanel }; from: { group: DockviewGroupPanel };
to: { group: DockviewGroupPanel; position: Position }; to: { group: DockviewGroupPanel; position: Position };
@ -285,8 +269,9 @@ export interface IDockviewComponent extends IBaseGrid<DockviewGroupPanel> {
readonly onDidRemoveGroup: Event<DockviewGroupPanel>; readonly onDidRemoveGroup: Event<DockviewGroupPanel>;
readonly onDidAddGroup: Event<DockviewGroupPanel>; readonly onDidAddGroup: Event<DockviewGroupPanel>;
readonly onDidActiveGroupChange: Event<DockviewGroupPanel | undefined>; readonly onDidActiveGroupChange: Event<DockviewGroupPanel | undefined>;
readonly onUnhandledDragOverEvent: Event<DockviewDndOverlayEvent>;
readonly options: DockviewComponentOptions; readonly options: DockviewComponentOptions;
updateOptions(options: DockviewComponentUpdateOptions): void; updateOptions(options: DockviewOptions): void;
moveGroupOrPanel(options: MoveGroupOrPanelOptions): void; moveGroupOrPanel(options: MoveGroupOrPanelOptions): void;
moveGroup(options: MoveGroupOptions): void; moveGroup(options: MoveGroupOptions): void;
doSetGroupActive: (group: DockviewGroupPanel, skipFocus?: boolean) => void; doSetGroupActive: (group: DockviewGroupPanel, skipFocus?: boolean) => void;
@ -353,6 +338,11 @@ export class DockviewComponent
readonly onWillShowOverlay: Event<WillShowOverlayLocationEvent> = readonly onWillShowOverlay: Event<WillShowOverlayLocationEvent> =
this._onWillShowOverlay.event; this._onWillShowOverlay.event;
private readonly _onUnhandledDragOverEvent =
new Emitter<DockviewDndOverlayEvent>();
readonly onUnhandledDragOverEvent: Event<DockviewDndOverlayEvent> =
this._onUnhandledDragOverEvent.event;
private readonly _onDidRemovePanel = new Emitter<IDockviewPanel>(); private readonly _onDidRemovePanel = new Emitter<IDockviewPanel>();
readonly onDidRemovePanel: Event<IDockviewPanel> = readonly onDidRemovePanel: Event<IDockviewPanel> =
this._onDidRemovePanel.event; this._onDidRemovePanel.event;
@ -431,8 +421,10 @@ export class DockviewComponent
constructor(options: DockviewComponentOptions) { constructor(options: DockviewComponentOptions) {
super({ super({
proportionalLayout: true, proportionalLayout: true,
orientation: options.orientation ?? Orientation.HORIZONTAL, orientation: Orientation.HORIZONTAL,
styles: options.styles, styles: options.hideBorders
? { separatorBorder: 'transparent' }
: undefined,
parentElement: options.parentElement, parentElement: options.parentElement,
disableAutoResizing: options.disableAutoResizing, disableAutoResizing: options.disableAutoResizing,
locked: options.locked, locked: options.locked,
@ -462,6 +454,7 @@ export class DockviewComponent
this._onDidAddGroup, this._onDidAddGroup,
this._onDidRemoveGroup, this._onDidRemoveGroup,
this._onDidActiveGroupChange, this._onDidActiveGroupChange,
this._onUnhandledDragOverEvent,
this.onDidAdd((event) => { this.onDidAdd((event) => {
if (!this._moving) { if (!this._moving) {
this._onDidAddGroup.fire(event); this._onDidAddGroup.fire(event);
@ -542,7 +535,6 @@ export class DockviewComponent
return true; return true;
} }
if (this.options.showDndOverlay) {
if (position === 'center' && this.gridview.length !== 0) { if (position === 'center' && this.gridview.length !== 0) {
/** /**
* for external events only show the four-corner drag overlays, disable * for external events only show the four-corner drag overlays, disable
@ -552,15 +544,16 @@ export class DockviewComponent
return false; return false;
} }
return this.options.showDndOverlay({ const firedEvent = new DockviewUnhandledDragOverEvent(
nativeEvent: event, event,
position: position, 'edge',
target: 'edge', position,
getData: getPanelData, getPanelData
}); );
}
return false; this._onUnhandledDragOverEvent.fire(firedEvent);
return firedEvent.isAccepted;
}, },
acceptedTargetZones: ['top', 'bottom', 'left', 'right', 'center'], acceptedTargetZones: ['top', 'bottom', 'left', 'right', 'center'],
overlayModel: overlayModel:
@ -1043,10 +1036,7 @@ export class DockviewComponent
} }
} }
updateOptions(options: DockviewComponentUpdateOptions): void { updateOptions(options: Partial<DockviewComponentOptions>): void {
const changed_orientation =
typeof options.orientation === 'string' &&
this.gridview.orientation !== options.orientation;
const changed_floatingGroupBounds = const changed_floatingGroupBounds =
options.floatingGroupBounds !== undefined && options.floatingGroupBounds !== undefined &&
options.floatingGroupBounds !== this.options.floatingGroupBounds; options.floatingGroupBounds !== this.options.floatingGroupBounds;
@ -1057,10 +1047,6 @@ export class DockviewComponent
this._options = { ...this.options, ...options }; this._options = { ...this.options, ...options };
if (changed_orientation) {
this.gridview.orientation = options.orientation!;
}
if (changed_floatingGroupBounds) { if (changed_floatingGroupBounds) {
for (const group of this._floatingGroups) { for (const group of this._floatingGroups) {
switch (this.options.floatingGroupBounds) { switch (this.options.floatingGroupBounds) {
@ -2153,6 +2139,9 @@ export class DockviewComponent
this._onWillShowOverlay.fire(event); this._onWillShowOverlay.fire(event);
}), }),
view.model.onUnhandledDragOverEvent((event) => {
this._onUnhandledDragOverEvent.fire(event);
}),
view.model.onDidAddPanel((event) => { view.model.onDidAddPanel((event) => {
if (this._moving) { if (this._moving) {
return; return;

View File

@ -26,7 +26,11 @@ import {
import { IWatermarkRenderer } from './types'; import { IWatermarkRenderer } from './types';
import { DockviewGroupPanel } from './dockviewGroupPanel'; import { DockviewGroupPanel } from './dockviewGroupPanel';
import { IDockviewPanel } from './dockviewPanel'; import { IDockviewPanel } from './dockviewPanel';
import { IHeaderActionsRenderer } from './options'; import {
DockviewDndOverlayEvent,
DockviewUnhandledDragOverEvent,
IHeaderActionsRenderer,
} from './options';
import { OverlayRenderContainer } from '../overlayRenderContainer'; import { OverlayRenderContainer } from '../overlayRenderContainer';
interface GroupMoveEvent { interface GroupMoveEvent {
@ -281,6 +285,11 @@ export class DockviewGroupPanelModel
readonly onDidActivePanelChange: Event<DockviewGroupChangeEvent> = readonly onDidActivePanelChange: Event<DockviewGroupChangeEvent> =
this._onDidActivePanelChange.event; this._onDidActivePanelChange.event;
private readonly _onUnhandledDragOverEvent =
new Emitter<DockviewDndOverlayEvent>();
readonly onUnhandledDragOverEvent: Event<DockviewDndOverlayEvent> =
this._onUnhandledDragOverEvent.event;
private readonly _api: DockviewApi; private readonly _api: DockviewApi;
get element(): HTMLElement { get element(): HTMLElement {
@ -458,7 +467,8 @@ export class DockviewGroupPanelModel
this._onWillDrop, this._onWillDrop,
this._onDidAddPanel, this._onDidAddPanel,
this._onDidRemovePanel, this._onDidRemovePanel,
this._onDidActivePanelChange this._onDidActivePanelChange,
this._onUnhandledDragOverEvent
); );
} }
@ -503,9 +513,9 @@ export class DockviewGroupPanelModel
this.setActive(this.isActive, true); this.setActive(this.isActive, true);
this.updateContainer(); this.updateContainer();
if (this.accessor.options.createRightHeaderActionsElement) { if (this.accessor.options.headerRightActionComponent) {
this._rightHeaderActions = this._rightHeaderActions =
this.accessor.options.createRightHeaderActionsElement( this.accessor.options.headerRightActionComponent(
this.groupPanel this.groupPanel
); );
this.addDisposables(this._rightHeaderActions); this.addDisposables(this._rightHeaderActions);
@ -518,9 +528,9 @@ export class DockviewGroupPanelModel
); );
} }
if (this.accessor.options.createLeftHeaderActionsElement) { if (this.accessor.options.headerLeftActionComponent) {
this._leftHeaderActions = this._leftHeaderActions =
this.accessor.options.createLeftHeaderActionsElement( this.accessor.options.headerLeftActionComponent(
this.groupPanel this.groupPanel
); );
this.addDisposables(this._leftHeaderActions); this.addDisposables(this._leftHeaderActions);
@ -533,9 +543,9 @@ export class DockviewGroupPanelModel
); );
} }
if (this.accessor.options.createPrefixHeaderActionsElement) { if (this.accessor.options.headerPrefixActionComponent) {
this._prefixHeaderActions = this._prefixHeaderActions =
this.accessor.options.createPrefixHeaderActionsElement( this.accessor.options.headerPrefixActionComponent(
this.groupPanel this.groupPanel
); );
this.addDisposables(this._prefixHeaderActions); this.addDisposables(this._prefixHeaderActions);
@ -926,17 +936,17 @@ export class DockviewGroupPanelModel
position: Position, position: Position,
target: DockviewGroupDropLocation target: DockviewGroupDropLocation
): boolean { ): boolean {
// custom overlay handler const firedEvent = new DockviewUnhandledDragOverEvent(
if (this.accessor.options.showDndOverlay) { event,
return this.accessor.options.showDndOverlay({
nativeEvent: event,
target, target,
group: this.accessor.getPanel(this.id)!,
position, position,
getData: getPanelData, getPanelData,
}); this.accessor.getPanel(this.id)!
} );
return false;
this._onUnhandledDragOverEvent.fire(firedEvent);
return firedEvent.isAccepted;
} }
private handleDropEvent( private handleDropEvent(

View File

@ -44,7 +44,7 @@ export class DockviewPanelModel implements IDockviewPanelModel {
} }
init(params: GroupPanelPartInitParameters): void { init(params: GroupPanelPartInitParameters): void {
this.content.init({ ...params, tab: this.tab }); this.content.init(params);
this.tab.init(params); this.tab.init(params);
} }

View File

@ -0,0 +1,42 @@
import { DockviewApi } from '../api/component.api';
import { DockviewGroupPanelApi } from '../api/dockviewGroupPanelApi';
import { DockviewPanelApi } from '../api/dockviewPanelApi';
import { PanelParameters } from '../framwork';
import { DockviewGroupPanel, IDockviewGroupPanel } from './dockviewGroupPanel';
import { IDockviewPanel } from './dockviewPanel';
export interface IGroupPanelBaseProps<T extends { [index: string]: any } = any>
extends PanelParameters<T> {
api: DockviewPanelApi;
containerApi: DockviewApi;
}
export type IDockviewPanelHeaderProps<
T extends { [index: string]: any } = any
> = IGroupPanelBaseProps<T>;
export type IDockviewPanelProps<T extends { [index: string]: any } = any> =
IGroupPanelBaseProps<T>;
export interface IDockviewHeaderActionsProps {
api: DockviewGroupPanelApi;
containerApi: DockviewApi;
panels: IDockviewPanel[];
activePanel: IDockviewPanel | undefined;
isGroupActive: boolean;
group: DockviewGroupPanel;
}
export interface IGroupHeaderProps {
api: DockviewGroupPanelApi;
containerApi: DockviewApi;
}
export interface IWatermarkPanelProps {
containerApi: DockviewApi;
group?: IDockviewGroupPanel;
}
export interface DockviewReadyEvent {
api: DockviewApi;
}

View File

@ -9,25 +9,24 @@ import {
} from './types'; } from './types';
import { Parameters } from '../panel/types'; import { Parameters } from '../panel/types';
import { DockviewGroupPanel } from './dockviewGroupPanel'; import { DockviewGroupPanel } from './dockviewGroupPanel';
import { ISplitviewStyles, Orientation } from '../splitview/splitview';
import { PanelTransfer } from '../dnd/dataTransfer'; import { PanelTransfer } from '../dnd/dataTransfer';
import { IDisposable } from '../lifecycle'; import { IDisposable } from '../lifecycle';
import { DroptargetOverlayModel, Position } from '../dnd/droptarget'; import { DroptargetOverlayModel, Position } from '../dnd/droptarget';
import { DockviewGroupDropLocation, GroupOptions } from './dockviewGroupPanelModel'; import {
DockviewGroupDropLocation,
GroupOptions,
} from './dockviewGroupPanelModel';
import { IDockviewPanel } from './dockviewPanel'; import { IDockviewPanel } from './dockviewPanel';
import { import {
ComponentConstructor, ComponentConstructor,
FrameworkFactory, FrameworkFactory,
} from '../panel/componentFactory'; } from '../panel/componentFactory';
import { DockviewGroupPanelApi } from '../api/dockviewGroupPanelApi';
import { DockviewPanelRenderer } from '../overlayRenderContainer'; import { DockviewPanelRenderer } from '../overlayRenderContainer';
import { IGroupHeaderProps } from './framework';
export interface IHeaderActionsRenderer extends IDisposable { export interface IHeaderActionsRenderer extends IDisposable {
readonly element: HTMLElement; readonly element: HTMLElement;
init(params: { init(params: IGroupHeaderProps): void;
containerApi: DockviewApi;
api: DockviewGroupPanelApi;
}): void;
} }
export interface GroupPanelFrameworkComponentFactory { export interface GroupPanelFrameworkComponentFactory {
@ -42,54 +41,15 @@ export interface TabContextMenuEvent {
panel: IDockviewPanel; panel: IDockviewPanel;
} }
export interface DockviewRenderFunctions {
tabComponents?: {
[componentName: string]: ComponentConstructor<ITabRenderer>;
};
components?: {
[componentName: string]: ComponentConstructor<IContentRenderer>;
};
frameworkTabComponents?: {
[componentName: string]: any;
};
frameworkComponents?: {
[componentName: string]: any;
};
}
export interface ViewFactoryData { export interface ViewFactoryData {
content: string; content: string;
tab?: string; tab?: string;
} }
export interface DockviewDndOverlayEvent { export interface DockviewOptions {
nativeEvent: DragEvent;
target: DockviewGroupDropLocation;
position: Position;
group?: DockviewGroupPanel;
getData: () => PanelTransfer | undefined;
}
export interface DockviewComponentOptions extends DockviewRenderFunctions {
disableAutoResizing?: boolean; disableAutoResizing?: boolean;
watermarkComponent?: WatermarkConstructor; hideBorders?: boolean;
watermarkFrameworkComponent?: any;
frameworkComponentFactory?: GroupPanelFrameworkComponentFactory;
orientation?: Orientation;
styles?: ISplitviewStyles;
defaultTabComponent?: string;
showDndOverlay?: (event: DockviewDndOverlayEvent) => boolean;
createRightHeaderActionsElement?: (
group: DockviewGroupPanel
) => IHeaderActionsRenderer;
createLeftHeaderActionsElement?: (
group: DockviewGroupPanel
) => IHeaderActionsRenderer;
createPrefixHeaderActionsElement?: (
group: DockviewGroupPanel
) => IHeaderActionsRenderer;
singleTabMode?: 'fullwidth' | 'default'; singleTabMode?: 'fullwidth' | 'default';
parentElement: HTMLElement;
disableFloatingGroups?: boolean; disableFloatingGroups?: boolean;
floatingGroupBounds?: floatingGroupBounds?:
| 'boundedWithinViewport' | 'boundedWithinViewport'
@ -105,6 +65,108 @@ export interface DockviewComponentOptions extends DockviewRenderFunctions {
disableDnd?: boolean; disableDnd?: boolean;
} }
export interface DockviewDndOverlayEvent {
nativeEvent: DragEvent;
target: DockviewGroupDropLocation;
position: Position;
group?: DockviewGroupPanel;
getData: () => PanelTransfer | undefined;
//
isAccepted: boolean;
accept(): void;
}
export class DockviewUnhandledDragOverEvent implements DockviewDndOverlayEvent {
private _isAccepted = false;
get isAccepted(): boolean {
return this._isAccepted;
}
constructor(
readonly nativeEvent: DragEvent,
readonly target: DockviewGroupDropLocation,
readonly position: Position,
readonly getData: () => PanelTransfer | undefined,
readonly group?: DockviewGroupPanel
) {}
accept(): void {
this._isAccepted = true;
}
}
export interface DockviewEvents {
// noop
}
export const PROPERTY_KEYS: (keyof DockviewOptions)[] = (() => {
/**
* by readong the keys from an empty value object TypeScript will error
* when we add or remove new properties to `DockviewOptions`
*/
const properties: Record<keyof DockviewOptions, undefined> = {
disableAutoResizing: undefined,
hideBorders: undefined,
singleTabMode: undefined,
disableFloatingGroups: undefined,
floatingGroupBounds: undefined,
popoutUrl: undefined,
defaultRenderer: undefined,
debug: undefined,
rootOverlayModel: undefined,
locked: undefined,
disableDnd: undefined,
};
return Object.keys(properties) as (keyof DockviewOptions)[];
})();
export const EVENT_KEYS: (keyof DockviewEvents)[] = (() => {
/**
* by readong the keys from an empty value object TypeScript will error
* when we add or remove new properties to `DockviewOptions`
*/
const properties: Record<keyof DockviewEvents, undefined> = {
showDndOverlay: undefined,
};
return Object.keys(properties) as (keyof DockviewEvents)[];
})();
export interface DockviewFrameworkOptions {
headerRightActionComponent?: (
group: DockviewGroupPanel
) => IHeaderActionsRenderer;
headerLeftActionComponent?: (
group: DockviewGroupPanel
) => IHeaderActionsRenderer;
headerPrefixActionComponent?: (
group: DockviewGroupPanel
) => IHeaderActionsRenderer;
tabComponents?: {
[componentName: string]: ComponentConstructor<ITabRenderer>;
};
components?: {
[componentName: string]: ComponentConstructor<IContentRenderer>;
};
frameworkTabComponents?: {
[componentName: string]: any;
};
frameworkComponents?: {
[componentName: string]: any;
};
parentElement: HTMLElement;
defaultTabComponent?: string;
watermarkComponent?: WatermarkConstructor;
watermarkFrameworkComponent?: any;
frameworkComponentFactory?: GroupPanelFrameworkComponentFactory;
}
export type DockviewComponentOptions = DockviewOptions &
DockviewEvents &
DockviewFrameworkOptions;
export interface PanelOptions<P extends object = Parameters> { export interface PanelOptions<P extends object = Parameters> {
component: string; component: string;
tabComponent?: string; tabComponent?: string;

View File

@ -18,11 +18,6 @@ export interface GroupPanelPartInitParameters
containerApi: DockviewApi; containerApi: DockviewApi;
} }
export interface GroupPanelContentPartInitParameters
extends GroupPanelPartInitParameters {
tab: ITabRenderer;
}
export interface WatermarkRendererInitParameters { export interface WatermarkRendererInitParameters {
containerApi: DockviewApi; containerApi: DockviewApi;
group?: IDockviewGroupPanel; group?: IDockviewGroupPanel;
@ -31,7 +26,7 @@ export interface WatermarkRendererInitParameters {
export interface IWatermarkRenderer export interface IWatermarkRenderer
extends Optional< extends Optional<
Omit<IPanel, 'id' | 'init'>, Omit<IPanel, 'id' | 'init'>,
'dispose' | 'update' | 'layout' | 'toJSON' 'dispose' | 'update' | 'layout' | 'toJSON' | 'focus'
> { > {
readonly element: HTMLElement; readonly element: HTMLElement;
init: (params: WatermarkRendererInitParameters) => void; init: (params: WatermarkRendererInitParameters) => void;
@ -41,7 +36,7 @@ export interface IWatermarkRenderer
export interface ITabRenderer export interface ITabRenderer
extends Optional< extends Optional<
Omit<IPanel, 'id'>, Omit<IPanel, 'id'>,
'dispose' | 'update' | 'layout' | 'toJSON' 'dispose' | 'update' | 'layout' | 'toJSON' | 'focus'
> { > {
readonly element: HTMLElement; readonly element: HTMLElement;
init(parameters: GroupPanelPartInitParameters): void; init(parameters: GroupPanelPartInitParameters): void;
@ -50,12 +45,10 @@ export interface ITabRenderer
export interface IContentRenderer export interface IContentRenderer
extends Optional< extends Optional<
Omit<IPanel, 'id'>, Omit<IPanel, 'id'>,
'dispose' | 'update' | 'layout' | 'toJSON' 'dispose' | 'update' | 'layout' | 'toJSON' | 'focus'
> { > {
readonly element: HTMLElement; readonly element: HTMLElement;
readonly onDidFocus?: Event<void>; init(parameters: GroupPanelPartInitParameters): void;
readonly onDidBlur?: Event<void>;
init(parameters: GroupPanelContentPartInitParameters): void;
} }
// watermark component // watermark component

View File

@ -0,0 +1,6 @@
import { Parameters } from "./panel/types";
export interface PanelParameters<T extends {} = Parameters> {
params: T;
}

View File

@ -23,19 +23,28 @@ export {
export * from './paneview/paneview'; export * from './paneview/paneview';
export * from './gridview/gridview'; export * from './gridview/gridview';
export { GridviewComponentOptions } from './gridview/options'; export { GridviewComponentOptions } from './gridview/options';
export * from './dockview/dockviewGroupPanelModel';
export * from './gridview/baseComponentGridview'; export * from './gridview/baseComponentGridview';
export * from './paneview/draggablePaneviewPanel'; export * from './paneview/draggablePaneviewPanel';
export * from './dockview/components/panel/content'; export * from './dockview/components/panel/content';
export * from './dockview/components/tab/tab'; export * from './dockview/components/tab/tab';
export * from './dockview/dockviewGroupPanelModel';
export { export {
TabDragEvent, TabDragEvent,
GroupDragEvent, GroupDragEvent,
} from './dockview/components/titlebar/tabsContainer'; } from './dockview/components/titlebar/tabsContainer';
export * from './dockview/types'; export * from './dockview/types';
export * from './dockview/dockviewGroupPanel'; export * from './dockview/dockviewGroupPanel';
export {
IGroupPanelBaseProps,
IDockviewPanelHeaderProps,
IDockviewPanelProps,
IDockviewHeaderActionsProps,
IGroupHeaderProps,
IWatermarkPanelProps,
DockviewReadyEvent,
} from './dockview/framework';
export * from './dockview/options'; export * from './dockview/options';
export * from './dockview/dockviewPanel'; export * from './dockview/dockviewPanel';

View File

@ -0,0 +1,15 @@
/* eslint-env node */
require('@rushstack/eslint-patch/modern-module-resolution')
module.exports = {
root: true,
'extends': [
'plugin:vue/vue3-essential',
'eslint:recommended',
'@vue/eslint-config-typescript',
'@vue/eslint-config-prettier/skip-formatting'
],
parserOptions: {
ecmaVersion: 'latest'
}
}

View File

@ -0,0 +1,30 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
.DS_Store
dist
dist-ssr
coverage
*.local
/cypress/videos/
/cypress/screenshots/
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
*.tsbuildinfo

View File

@ -0,0 +1,8 @@
{
"$schema": "https://json.schemastore.org/prettierrc",
"semi": false,
"tabWidth": 2,
"singleQuote": true,
"printWidth": 100,
"trailingComma": "none"
}

View File

@ -0,0 +1,8 @@
{
"recommendations": [
"Vue.volar",
"Vue.vscode-typescript-vue-plugin",
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode"
]
}

View File

@ -0,0 +1,52 @@
# vue-project
This template should help get you started developing with Vue 3 in Vite.
## Recommended IDE Setup
[VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin).
## Type Support for `.vue` Imports in TS
TypeScript cannot handle type information for `.vue` imports by default, so we replace the `tsc` CLI with `vue-tsc` for type checking. In editors, we need [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin) to make the TypeScript language service aware of `.vue` types.
If the standalone TypeScript plugin doesn't feel fast enough to you, Volar has also implemented a [Take Over Mode](https://github.com/johnsoncodehk/volar/discussions/471#discussioncomment-1361669) that is more performant. You can enable it by the following steps:
1. Disable the built-in TypeScript Extension
1) Run `Extensions: Show Built-in Extensions` from VSCode's command palette
2) Find `TypeScript and JavaScript Language Features`, right click and select `Disable (Workspace)`
2. Reload the VSCode window by running `Developer: Reload Window` from the command palette.
## Customize configuration
See [Vite Configuration Reference](https://vitejs.dev/config/).
## Project Setup
```sh
npm install
```
### Compile and Hot-Reload for Development
```sh
npm run dev
```
### Type-Check, Compile and Minify for Production
```sh
npm run build
```
### Run Unit Tests with [Vitest](https://vitest.dev/)
```sh
npm run test:unit
```
### Lint with [ESLint](https://eslint.org/)
```sh
npm run lint
```

View File

@ -0,0 +1 @@
/// <reference types="vite/client" />

View File

@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="icon" href="/favicon.ico">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vite App</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>

View File

@ -0,0 +1,35 @@
{
"name": "vue-project",
"version": "0.0.0",
"private": true,
"type": "module",
"scripts": {
"dev": "vite",
"build": "run-p type-check \"build-only {@}\" --",
"preview": "vite preview",
"test:unit": "vitest",
"build-only": "vite build",
"type-check": "vue-tsc --build --force",
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore",
"format": "prettier --write src/"
},
"dependencies": {
"pinia": "^2.1.7",
"vue-router": "^4.2.5",
"dockview-vue": "*"
},
"devDependencies": {
"@rushstack/eslint-patch": "^1.3.3",
"@tsconfig/node20": "^20.1.2",
"@types/jsdom": "^21.1.6",
"@types/node": "^20.11.10",
"@vitejs/plugin-vue-jsx": "^3.1.0",
"@vue/eslint-config-prettier": "^8.0.0",
"@vue/eslint-config-typescript": "^12.0.0",
"@vue/test-utils": "^2.4.4",
"eslint-plugin-vue": "^9.17.0",
"npm-run-all2": "^6.1.1",
"prettier": "^3.0.3",
"vitest": "^1.2.2"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@ -0,0 +1,87 @@
<script setup lang="ts">
import { RouterLink, RouterView } from 'vue-router'
import HelloWorld from './components/HelloWorld.vue'
import TestView from './views/TestView.vue'
</script>
<template>
<!-- <header>
<img alt="Vue logo" class="logo" src="@/assets/logo.svg" width="125" height="125" />
<div class="wrapper">
<HelloWorld msg="You did it!" />
<nav>
<RouterLink to="/">Home</RouterLink>
<RouterLink to="/about">About</RouterLink>
</nav>
</div>
</header> -->
<TestView/>
<!-- <RouterView /> -->
</template>
<style scoped>
header {
line-height: 1.5;
max-height: 100vh;
}
.logo {
display: block;
margin: 0 auto 2rem;
}
nav {
width: 100%;
font-size: 12px;
text-align: center;
margin-top: 2rem;
}
nav a.router-link-exact-active {
color: var(--color-text);
}
nav a.router-link-exact-active:hover {
background-color: transparent;
}
nav a {
display: inline-block;
padding: 0 1rem;
border-left: 1px solid var(--color-border);
}
nav a:first-of-type {
border: 0;
}
@media (min-width: 1024px) {
header {
display: flex;
place-items: center;
padding-right: calc(var(--section-gap) / 2);
}
.logo {
margin: 0 2rem 0 0;
}
header .wrapper {
display: flex;
place-items: flex-start;
flex-wrap: wrap;
}
nav {
text-align: left;
margin-left: -1rem;
font-size: 1rem;
padding: 1rem 0;
margin-top: 1rem;
}
}
</style>

View File

@ -0,0 +1,86 @@
/* color palette from <https://github.com/vuejs/theme> */
:root {
--vt-c-white: #ffffff;
--vt-c-white-soft: #f8f8f8;
--vt-c-white-mute: #f2f2f2;
--vt-c-black: #181818;
--vt-c-black-soft: #222222;
--vt-c-black-mute: #282828;
--vt-c-indigo: #2c3e50;
--vt-c-divider-light-1: rgba(60, 60, 60, 0.29);
--vt-c-divider-light-2: rgba(60, 60, 60, 0.12);
--vt-c-divider-dark-1: rgba(84, 84, 84, 0.65);
--vt-c-divider-dark-2: rgba(84, 84, 84, 0.48);
--vt-c-text-light-1: var(--vt-c-indigo);
--vt-c-text-light-2: rgba(60, 60, 60, 0.66);
--vt-c-text-dark-1: var(--vt-c-white);
--vt-c-text-dark-2: rgba(235, 235, 235, 0.64);
}
/* semantic color variables for this project */
:root {
--color-background: var(--vt-c-white);
--color-background-soft: var(--vt-c-white-soft);
--color-background-mute: var(--vt-c-white-mute);
--color-border: var(--vt-c-divider-light-2);
--color-border-hover: var(--vt-c-divider-light-1);
--color-heading: var(--vt-c-text-light-1);
--color-text: var(--vt-c-text-light-1);
--section-gap: 160px;
}
@media (prefers-color-scheme: dark) {
:root {
--color-background: var(--vt-c-black);
--color-background-soft: var(--vt-c-black-soft);
--color-background-mute: var(--vt-c-black-mute);
--color-border: var(--vt-c-divider-dark-2);
--color-border-hover: var(--vt-c-divider-dark-1);
--color-heading: var(--vt-c-text-dark-1);
--color-text: var(--vt-c-text-dark-2);
}
}
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
font-weight: normal;
}
body {
min-height: 100vh;
color: var(--color-text);
background: var(--color-background);
transition:
color 0.5s,
background-color 0.5s;
line-height: 1.6;
font-family:
Inter,
-apple-system,
BlinkMacSystemFont,
'Segoe UI',
Roboto,
Oxygen,
Ubuntu,
Cantarell,
'Fira Sans',
'Droid Sans',
'Helvetica Neue',
sans-serif;
font-size: 15px;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 261.76 226.69"><path d="M161.096.001l-30.225 52.351L100.647.001H-.005l130.877 226.688L261.749.001z" fill="#41b883"/><path d="M161.096.001l-30.225 52.351L100.647.001H52.346l78.526 136.01L209.398.001z" fill="#34495e"/></svg>

After

Width:  |  Height:  |  Size: 276 B

View File

@ -0,0 +1,35 @@
@import './base.css';
#app {
max-width: 1280px;
margin: 0 auto;
padding: 2rem;
font-weight: normal;
}
a,
.green {
text-decoration: none;
color: hsla(160, 100%, 37%, 1);
transition: 0.4s;
padding: 3px;
}
@media (hover: hover) {
a:hover {
background-color: hsla(160, 100%, 37%, 0.2);
}
}
@media (min-width: 1024px) {
body {
display: flex;
place-items: center;
}
#app {
display: grid;
grid-template-columns: 1fr 1fr;
padding: 0 2rem;
}
}

View File

@ -0,0 +1,41 @@
<script setup lang="ts">
defineProps<{
msg: string
}>()
</script>
<template>
<div class="greetings">
<h1 class="green">{{ msg }}</h1>
<h3>
Youve successfully created a project with
<a href="https://vitejs.dev/" target="_blank" rel="noopener">Vite</a> +
<a href="https://vuejs.org/" target="_blank" rel="noopener">Vue 3</a>. What's next?
</h3>
</div>
</template>
<style scoped>
h1 {
font-weight: 500;
font-size: 2.6rem;
position: relative;
top: -10px;
}
h3 {
font-size: 1.2rem;
}
.greetings h1,
.greetings h3 {
text-align: center;
}
@media (min-width: 1024px) {
.greetings h1,
.greetings h3 {
text-align: left;
}
}
</style>

View File

@ -0,0 +1,88 @@
<script setup lang="ts">
import WelcomeItem from './WelcomeItem.vue'
import DocumentationIcon from './icons/IconDocumentation.vue'
import ToolingIcon from './icons/IconTooling.vue'
import EcosystemIcon from './icons/IconEcosystem.vue'
import CommunityIcon from './icons/IconCommunity.vue'
import SupportIcon from './icons/IconSupport.vue'
</script>
<template>
<WelcomeItem>
<template #icon>
<DocumentationIcon />
</template>
<template #heading>Documentation</template>
Vues
<a href="https://vuejs.org/" target="_blank" rel="noopener">official documentation</a>
provides you with all information you need to get started.
</WelcomeItem>
<WelcomeItem>
<template #icon>
<ToolingIcon />
</template>
<template #heading>Tooling</template>
This project is served and bundled with
<a href="https://vitejs.dev/guide/features.html" target="_blank" rel="noopener">Vite</a>. The
recommended IDE setup is
<a href="https://code.visualstudio.com/" target="_blank" rel="noopener">VSCode</a> +
<a href="https://github.com/johnsoncodehk/volar" target="_blank" rel="noopener">Volar</a>. If
you need to test your components and web pages, check out
<a href="https://www.cypress.io/" target="_blank" rel="noopener">Cypress</a> and
<a href="https://on.cypress.io/component" target="_blank" rel="noopener"
>Cypress Component Testing</a
>.
<br />
More instructions are available in <code>README.md</code>.
</WelcomeItem>
<WelcomeItem>
<template #icon>
<EcosystemIcon />
</template>
<template #heading>Ecosystem</template>
Get official tools and libraries for your project:
<a href="https://pinia.vuejs.org/" target="_blank" rel="noopener">Pinia</a>,
<a href="https://router.vuejs.org/" target="_blank" rel="noopener">Vue Router</a>,
<a href="https://test-utils.vuejs.org/" target="_blank" rel="noopener">Vue Test Utils</a>, and
<a href="https://github.com/vuejs/devtools" target="_blank" rel="noopener">Vue Dev Tools</a>. If
you need more resources, we suggest paying
<a href="https://github.com/vuejs/awesome-vue" target="_blank" rel="noopener">Awesome Vue</a>
a visit.
</WelcomeItem>
<WelcomeItem>
<template #icon>
<CommunityIcon />
</template>
<template #heading>Community</template>
Got stuck? Ask your question on
<a href="https://chat.vuejs.org" target="_blank" rel="noopener">Vue Land</a>, our official
Discord server, or
<a href="https://stackoverflow.com/questions/tagged/vue.js" target="_blank" rel="noopener"
>StackOverflow</a
>. You should also subscribe to
<a href="https://news.vuejs.org" target="_blank" rel="noopener">our mailing list</a> and follow
the official
<a href="https://twitter.com/vuejs" target="_blank" rel="noopener">@vuejs</a>
twitter account for latest news in the Vue world.
</WelcomeItem>
<WelcomeItem>
<template #icon>
<SupportIcon />
</template>
<template #heading>Support Vue</template>
As an independent project, Vue relies on community backing for its sustainability. You can help
us by
<a href="https://vuejs.org/sponsor/" target="_blank" rel="noopener">becoming a sponsor</a>.
</WelcomeItem>
</template>

View File

@ -0,0 +1,87 @@
<template>
<div class="item">
<i>
<slot name="icon"></slot>
</i>
<div class="details">
<h3>
<slot name="heading"></slot>
</h3>
<slot></slot>
</div>
</div>
</template>
<style scoped>
.item {
margin-top: 2rem;
display: flex;
position: relative;
}
.details {
flex: 1;
margin-left: 1rem;
}
i {
display: flex;
place-items: center;
place-content: center;
width: 32px;
height: 32px;
color: var(--color-text);
}
h3 {
font-size: 1.2rem;
font-weight: 500;
margin-bottom: 0.4rem;
color: var(--color-heading);
}
@media (min-width: 1024px) {
.item {
margin-top: 0;
padding: 0.4rem 0 1rem calc(var(--section-gap) / 2);
}
i {
top: calc(50% - 25px);
left: -26px;
position: absolute;
border: 1px solid var(--color-border);
background: var(--color-background);
border-radius: 8px;
width: 50px;
height: 50px;
}
.item:before {
content: ' ';
border-left: 1px solid var(--color-border);
position: absolute;
left: 0;
bottom: calc(50% + 25px);
height: calc(50% - 25px);
}
.item:after {
content: ' ';
border-left: 1px solid var(--color-border);
position: absolute;
left: 0;
top: calc(50% + 25px);
height: calc(50% - 25px);
}
.item:first-of-type:before {
display: none;
}
.item:last-of-type:after {
display: none;
}
}
</style>

View File

@ -0,0 +1,11 @@
import { describe, it, expect } from 'vitest'
import { mount } from '@vue/test-utils'
import HelloWorld from '../HelloWorld.vue'
describe('HelloWorld', () => {
it('renders properly', () => {
const wrapper = mount(HelloWorld, { props: { msg: 'Hello Vitest' } })
expect(wrapper.text()).toContain('Hello Vitest')
})
})

View File

@ -0,0 +1,7 @@
<template>
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor">
<path
d="M15 4a1 1 0 1 0 0 2V4zm0 11v-1a1 1 0 0 0-1 1h1zm0 4l-.707.707A1 1 0 0 0 16 19h-1zm-4-4l.707-.707A1 1 0 0 0 11 14v1zm-4.707-1.293a1 1 0 0 0-1.414 1.414l1.414-1.414zm-.707.707l-.707-.707.707.707zM9 11v-1a1 1 0 0 0-.707.293L9 11zm-4 0h1a1 1 0 0 0-1-1v1zm0 4H4a1 1 0 0 0 1.707.707L5 15zm10-9h2V4h-2v2zm2 0a1 1 0 0 1 1 1h2a3 3 0 0 0-3-3v2zm1 1v6h2V7h-2zm0 6a1 1 0 0 1-1 1v2a3 3 0 0 0 3-3h-2zm-1 1h-2v2h2v-2zm-3 1v4h2v-4h-2zm1.707 3.293l-4-4-1.414 1.414 4 4 1.414-1.414zM11 14H7v2h4v-2zm-4 0c-.276 0-.525-.111-.707-.293l-1.414 1.414C5.42 15.663 6.172 16 7 16v-2zm-.707 1.121l3.414-3.414-1.414-1.414-3.414 3.414 1.414 1.414zM9 12h4v-2H9v2zm4 0a3 3 0 0 0 3-3h-2a1 1 0 0 1-1 1v2zm3-3V3h-2v6h2zm0-6a3 3 0 0 0-3-3v2a1 1 0 0 1 1 1h2zm-3-3H3v2h10V0zM3 0a3 3 0 0 0-3 3h2a1 1 0 0 1 1-1V0zM0 3v6h2V3H0zm0 6a3 3 0 0 0 3 3v-2a1 1 0 0 1-1-1H0zm3 3h2v-2H3v2zm1-1v4h2v-4H4zm1.707 4.707l.586-.586-1.414-1.414-.586.586 1.414 1.414z"
/>
</svg>
</template>

View File

@ -0,0 +1,7 @@
<template>
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="17" fill="currentColor">
<path
d="M11 2.253a1 1 0 1 0-2 0h2zm-2 13a1 1 0 1 0 2 0H9zm.447-12.167a1 1 0 1 0 1.107-1.666L9.447 3.086zM1 2.253L.447 1.42A1 1 0 0 0 0 2.253h1zm0 13H0a1 1 0 0 0 1.553.833L1 15.253zm8.447.833a1 1 0 1 0 1.107-1.666l-1.107 1.666zm0-14.666a1 1 0 1 0 1.107 1.666L9.447 1.42zM19 2.253h1a1 1 0 0 0-.447-.833L19 2.253zm0 13l-.553.833A1 1 0 0 0 20 15.253h-1zm-9.553-.833a1 1 0 1 0 1.107 1.666L9.447 14.42zM9 2.253v13h2v-13H9zm1.553-.833C9.203.523 7.42 0 5.5 0v2c1.572 0 2.961.431 3.947 1.086l1.107-1.666zM5.5 0C3.58 0 1.797.523.447 1.42l1.107 1.666C2.539 2.431 3.928 2 5.5 2V0zM0 2.253v13h2v-13H0zm1.553 13.833C2.539 15.431 3.928 15 5.5 15v-2c-1.92 0-3.703.523-5.053 1.42l1.107 1.666zM5.5 15c1.572 0 2.961.431 3.947 1.086l1.107-1.666C9.203 13.523 7.42 13 5.5 13v2zm5.053-11.914C11.539 2.431 12.928 2 14.5 2V0c-1.92 0-3.703.523-5.053 1.42l1.107 1.666zM14.5 2c1.573 0 2.961.431 3.947 1.086l1.107-1.666C18.203.523 16.421 0 14.5 0v2zm3.5.253v13h2v-13h-2zm1.553 12.167C18.203 13.523 16.421 13 14.5 13v2c1.573 0 2.961.431 3.947 1.086l1.107-1.666zM14.5 13c-1.92 0-3.703.523-5.053 1.42l1.107 1.666C11.539 15.431 12.928 15 14.5 15v-2z"
/>
</svg>
</template>

View File

@ -0,0 +1,7 @@
<template>
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="20" fill="currentColor">
<path
d="M11.447 8.894a1 1 0 1 0-.894-1.789l.894 1.789zm-2.894-.789a1 1 0 1 0 .894 1.789l-.894-1.789zm0 1.789a1 1 0 1 0 .894-1.789l-.894 1.789zM7.447 7.106a1 1 0 1 0-.894 1.789l.894-1.789zM10 9a1 1 0 1 0-2 0h2zm-2 2.5a1 1 0 1 0 2 0H8zm9.447-5.606a1 1 0 1 0-.894-1.789l.894 1.789zm-2.894-.789a1 1 0 1 0 .894 1.789l-.894-1.789zm2 .789a1 1 0 1 0 .894-1.789l-.894 1.789zm-1.106-2.789a1 1 0 1 0-.894 1.789l.894-1.789zM18 5a1 1 0 1 0-2 0h2zm-2 2.5a1 1 0 1 0 2 0h-2zm-5.447-4.606a1 1 0 1 0 .894-1.789l-.894 1.789zM9 1l.447-.894a1 1 0 0 0-.894 0L9 1zm-2.447.106a1 1 0 1 0 .894 1.789l-.894-1.789zm-6 3a1 1 0 1 0 .894 1.789L.553 4.106zm2.894.789a1 1 0 1 0-.894-1.789l.894 1.789zm-2-.789a1 1 0 1 0-.894 1.789l.894-1.789zm1.106 2.789a1 1 0 1 0 .894-1.789l-.894 1.789zM2 5a1 1 0 1 0-2 0h2zM0 7.5a1 1 0 1 0 2 0H0zm8.553 12.394a1 1 0 1 0 .894-1.789l-.894 1.789zm-1.106-2.789a1 1 0 1 0-.894 1.789l.894-1.789zm1.106 1a1 1 0 1 0 .894 1.789l-.894-1.789zm2.894.789a1 1 0 1 0-.894-1.789l.894 1.789zM8 19a1 1 0 1 0 2 0H8zm2-2.5a1 1 0 1 0-2 0h2zm-7.447.394a1 1 0 1 0 .894-1.789l-.894 1.789zM1 15H0a1 1 0 0 0 .553.894L1 15zm1-2.5a1 1 0 1 0-2 0h2zm12.553 2.606a1 1 0 1 0 .894 1.789l-.894-1.789zM17 15l.447.894A1 1 0 0 0 18 15h-1zm1-2.5a1 1 0 1 0-2 0h2zm-7.447-5.394l-2 1 .894 1.789 2-1-.894-1.789zm-1.106 1l-2-1-.894 1.789 2 1 .894-1.789zM8 9v2.5h2V9H8zm8.553-4.894l-2 1 .894 1.789 2-1-.894-1.789zm.894 0l-2-1-.894 1.789 2 1 .894-1.789zM16 5v2.5h2V5h-2zm-4.553-3.894l-2-1-.894 1.789 2 1 .894-1.789zm-2.894-1l-2 1 .894 1.789 2-1L8.553.106zM1.447 5.894l2-1-.894-1.789-2 1 .894 1.789zm-.894 0l2 1 .894-1.789-2-1-.894 1.789zM0 5v2.5h2V5H0zm9.447 13.106l-2-1-.894 1.789 2 1 .894-1.789zm0 1.789l2-1-.894-1.789-2 1 .894 1.789zM10 19v-2.5H8V19h2zm-6.553-3.894l-2-1-.894 1.789 2 1 .894-1.789zM2 15v-2.5H0V15h2zm13.447 1.894l2-1-.894-1.789-2 1 .894 1.789zM18 15v-2.5h-2V15h2z"
/>
</svg>
</template>

View File

@ -0,0 +1,7 @@
<template>
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor">
<path
d="M10 3.22l-.61-.6a5.5 5.5 0 0 0-7.666.105 5.5 5.5 0 0 0-.114 7.665L10 18.78l8.39-8.4a5.5 5.5 0 0 0-.114-7.665 5.5 5.5 0 0 0-7.666-.105l-.61.61z"
/>
</svg>
</template>

View File

@ -0,0 +1,19 @@
<!-- This icon is from <https://github.com/Templarian/MaterialDesign>, distributed under Apache 2.0 (https://www.apache.org/licenses/LICENSE-2.0) license-->
<template>
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
aria-hidden="true"
role="img"
class="iconify iconify--mdi"
width="24"
height="24"
preserveAspectRatio="xMidYMid meet"
viewBox="0 0 24 24"
>
<path
d="M20 18v-4h-3v1h-2v-1H9v1H7v-1H4v4h16M6.33 8l-1.74 4H7v-1h2v1h6v-1h2v1h2.41l-1.74-4H6.33M9 5v1h6V5H9m12.84 7.61c.1.22.16.48.16.8V18c0 .53-.21 1-.6 1.41c-.4.4-.85.59-1.4.59H4c-.55 0-1-.19-1.4-.59C2.21 19 2 18.53 2 18v-4.59c0-.32.06-.58.16-.8L4.5 7.22C4.84 6.41 5.45 6 6.33 6H7V5c0-.55.18-1 .57-1.41C7.96 3.2 8.44 3 9 3h6c.56 0 1.04.2 1.43.59c.39.41.57.86.57 1.41v1h.67c.88 0 1.49.41 1.83 1.22l2.34 5.39z"
fill="currentColor"
></path>
</svg>
</template>

View File

@ -0,0 +1,121 @@
<script setup lang="ts">
import {
DockviewApi,
DockviewComponent,
type DockviewDndOverlayEvent,
type DockviewPanelRenderer,
type DroptargetOverlayModel,
type IContentRenderer,
type ITabRenderer,
type IWatermarkRenderer
} from 'dockview-core'
import { ref, onMounted, watch, onBeforeUnmount } from 'vue'
import {
VueContentRenderer,
VueTabRenderer,
VueWatermarkRenderer,
type ComponentInterface
} from './utils'
interface Props {
// onReady: (event: DockviewReadyEvent) => void;
components: Record<string, any>
tabComponents?: Record<string, any>
// watermarkComponent?: React.FunctionComponent<IWatermarkPanelProps>;
// onDidDrop?: (event: DockviewDidDropEvent) => void;
// onWillDrop?: (event: DockviewWillDropEvent) => void;
// showDndOverlay?: (event: DockviewDndOverlayEvent) => boolean;
hideBorders?: boolean;
className?: string;
disableAutoResizing?: boolean;
// defaultTabComponent?: React.FunctionComponent<IDockviewPanelHeaderProps>;
// rightHeaderActionsComponent?: React.FunctionComponent<IDockviewHeaderActionsProps>;
// leftHeaderActionsComponent?: React.FunctionComponent<IDockviewHeaderActionsProps>;
// prefixHeaderActionsComponent?: React.FunctionComponent<IDockviewHeaderActionsProps>;
singleTabMode?: 'fullwidth' | 'default';
disableFloatingGroups?: boolean;
floatingGroupBounds?:
| 'boundedWithinViewport'
| {
minimumHeightWithinViewport?: number;
minimumWidthWithinViewport?: number;
};
debug?: boolean;
defaultRenderer?: DockviewPanelRenderer;
rootOverlayModel?: DroptargetOverlayModel
locked?: boolean;
disableDnd?: boolean;
}
interface Emits {
(event: 'ready', value: { api: DockviewApi }): void
(event: 'showDndOverlay', value: DockviewDndOverlayEvent):void
}
const props = defineProps<Props>()
const emit = defineEmits<Emits>()
const el = ref<HTMLElement | null>(null)
const instance = ref<DockviewComponent | null>(null)
watch(() => props.components, (newValue, oldValue) => {
if (instance.value) {
instance.value.updateOptions({ components: newValue })
}
})
onMounted(() => {
if (!el.value) {
throw new Error('element is not mounted')
}
const dockview = new DockviewComponent({
parentElement: el.value,
frameworkComponentFactory: {
content: {
createComponent: (id: string, componentId: string, component: any): IContentRenderer => {
console.log('dockview-vue: createComponent')
return new VueContentRenderer(component as ComponentInterface)
}
},
tab: {
createComponent: (id: string, componentId: string, component: any): ITabRenderer => {
return new VueTabRenderer(component as ComponentInterface)
}
},
watermark: {
createComponent: (id: string, componentId: string, component: any): IWatermarkRenderer => {
return new VueWatermarkRenderer(component as ComponentInterface)
}
}
},
frameworkComponents: props.components,
disableAutoResizing: props.disableAutoResizing,
frameworkTabComponents: props.tabComponents,
singleTabMode: props.singleTabMode,
disableFloatingGroups: props.disableFloatingGroups,
floatingGroupBounds: props.floatingGroupBounds,
defaultRenderer: props.defaultRenderer,
debug: props.debug,
rootOverlayModel: props.rootOverlayModel,
locked: props.locked,
disableDnd: props.disableDnd,
})
instance.value = dockview
emit("ready", { api: new DockviewApi(dockview) })
})
onBeforeUnmount(() => {
if (instance.value) {
instance.value.dispose()
}
})
</script>
<template>
<div ref="el" />
</template>

View File

@ -0,0 +1,127 @@
import { defineComponent, h, type ComponentOptionsBase, type PropType } from 'vue'
import {
DockviewComponent,
DockviewApi,
type IContentRenderer,
type ITabRenderer,
type IWatermarkRenderer
} from 'dockview-core'
import { VueContentRenderer, VueTabRenderer, VueWatermarkRenderer } from './utils'
type ComponentInterface = ComponentOptionsBase<any, any, any, any, any, any, any, any>
export interface DockviewReadyEvent {
api: DockviewApi
}
export type DVProps = {
readonly components: { [index: string]: any }
readonly tabComponents?: { [index: string]: any }
}
export type DVEmits = {
onReady(event: { api: DockviewApi }): void
}
type RawBindings = {}
// function mountComponent() {
// createVNode()
// }
export default defineComponent(
// <DVProps, RawBindings, {}, {}, {}, {}, {}, DVEmits>
{
props: {
components: {
type: Object as PropType<{ [index: string]: any }>,
required: true
},
tabComponents: Object as PropType<{ [index: string]: any }>
},
// type inference enabled
render() {
return h('div')
},
methods: {},
// setup(props, ctx) {
// // called once
// // return () => {
// // // called on re-render
// // }
// },
data() {
return {
count: 1
}
},
setup(props, ctx) {
// getCurrentInstance()
// console.log('props', props.keyA)
// watch(
// () => props.keyA,
// (a, b) => {
// console.log('asaa')
// }
// )
},
watch: {
props: {
handler(a, b) {
console.log('a,b')
}
}
},
mounted() {
const element = this.$el as HTMLElement
this.$props.components
console.log('dockview-vue: mounted vue wrapper', this.$props.components)
const instance = new DockviewComponent({
parentElement: element,
frameworkComponents: this.$props.components,
frameworkComponentFactory: {
content: {
createComponent: (
id: string,
componentId: string,
component: any
): IContentRenderer => {
console.log('dockview-vue: createComponent', this.$props.components)
return new VueContentRenderer(component as ComponentInterface)
}
},
tab: {
createComponent: (id: string, componentId: string, component: any): ITabRenderer => {
return new VueTabRenderer(component as ComponentInterface)
}
},
watermark: {
createComponent: (
id: string,
componentId: string,
component: any
): IWatermarkRenderer => {
return new VueWatermarkRenderer(component as ComponentInterface)
}
}
}
})
const api = new DockviewApi(instance)
this.$emit('ready', { api });
// this.$emit('')
// this.name // type: string | undefined
// this.msg // type: string
// this.count // type: number
}
}
)

View File

@ -0,0 +1,151 @@
import type {
DockviewGroupPanel,
GroupPanelPartInitParameters,
IContentRenderer,
ITabRenderer,
IWatermarkRenderer,
PanelUpdateEvent,
Parameters,
WatermarkRendererInitParameters
} from 'dockview-core'
import { createVNode, type ComponentOptionsBase, render, cloneVNode, mergeProps } from 'vue'
export type ComponentInterface = ComponentOptionsBase<any, any, any, any, any, any, any, any>
/**
* TODO List
*
* 1. handle vue context-ish stuff (appContext? provides?)
*
*
*
* @see https://vuejs.org/api/render-function.html#clonevnode
* @see https://vuejs.org/api/render-function.html#mergeprops
*/
export function mountVueComponent(component: ComponentInterface, props: any, element: HTMLElement) {
let vNode = createVNode(component, props)
render(vNode, element)
return {
update: (newProps: any) => {
vNode = cloneVNode(vNode, mergeProps(props, newProps))
render(vNode, element)
},
dispose: () => {
render(null, element)
}
}
}
export class VueContentRenderer implements IContentRenderer {
private _element: HTMLElement
private _renderDisposable: { update: (props: any) => void; dispose: () => void } | undefined
get element(): HTMLElement {
return this._element
}
constructor(private readonly component: ComponentInterface) {
this._element = document.createElement('div')
this.element.className = 'dv-vue-part'
}
init(parameters: GroupPanelPartInitParameters): void {
const props = {
params: parameters.params,
api: parameters.api,
containerApi: parameters.containerApi
}
this._renderDisposable?.dispose()
this._renderDisposable = mountVueComponent(this.component, props, this.element)
}
update(event: PanelUpdateEvent<Parameters>): void {
const params = event.params
// TODO: handle prop updates somehow?
this._renderDisposable?.update(params)
}
focus(): void {
// TODO: make optional on interface
}
dispose(): void {
this._renderDisposable?.dispose()
}
}
export class VueTabRenderer implements ITabRenderer {
private _element: HTMLElement
private _renderDisposable: { update: (props: any) => void; dispose: () => void } | undefined
get element(): HTMLElement {
return this._element
}
constructor(private readonly component: ComponentInterface) {
this._element = document.createElement('div')
this.element.className = 'dv-vue-part'
}
init(parameters: GroupPanelPartInitParameters): void {
const props = {
params: parameters.params,
api: parameters.api,
containerApi: parameters.containerApi
}
this._renderDisposable?.dispose()
this._renderDisposable = mountVueComponent(this.component, props, this.element)
}
update(event: PanelUpdateEvent<Parameters>): void {
const params = event.params
// TODO: handle prop updates somehow?
this._renderDisposable?.update(params)
}
dispose(): void {
this._renderDisposable?.dispose()
}
}
export class VueWatermarkRenderer implements IWatermarkRenderer {
private _element: HTMLElement
private _renderDisposable: { update: (props: any) => void; dispose: () => void } | undefined
get element(): HTMLElement {
return this._element
}
constructor(private readonly component: ComponentInterface) {
this._element = document.createElement('div')
this.element.className = 'dv-vue-part'
}
init(parameters: WatermarkRendererInitParameters): void {
const props = {
group: parameters.group,
containerApi: parameters.containerApi
}
this._renderDisposable?.dispose()
this._renderDisposable = mountVueComponent(this.component, props, this.element)
}
updateParentGroup(group: DockviewGroupPanel, visible: boolean): void {
// TODO: make optional on interface
}
update(event: PanelUpdateEvent<Parameters>): void {
const params = event.params
// TODO: handle prop updates somehow?
this._renderDisposable?.update(params)
}
dispose(): void {
this._renderDisposable?.dispose()
}
}

View File

@ -0,0 +1,14 @@
import './assets/main.css'
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
import router from './router'
const app = createApp(App)
app.use(createPinia())
app.use(router)
app.mount('#app')

View File

@ -0,0 +1,23 @@
import { createRouter, createWebHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: '/',
name: 'home',
component: HomeView
},
{
path: '/about',
name: 'about',
// route level code-splitting
// this generates a separate chunk (About.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import('../views/AboutView.vue')
}
]
})
export default router

View File

@ -0,0 +1,12 @@
import { ref, computed } from 'vue'
import { defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', () => {
const count = ref(0)
const doubleCount = computed(() => count.value * 2)
function increment() {
count.value++
}
return { count, doubleCount, increment }
})

View File

@ -0,0 +1,15 @@
<template>
<div class="about">
<h1>This is an about page</h1>
</div>
</template>
<style>
@media (min-width: 1024px) {
.about {
min-height: 100vh;
display: flex;
align-items: center;
}
}
</style>

View File

@ -0,0 +1,10 @@
<script setup lang="ts">
defineEmits({});
</script>
<template>
<main>
<div>hi</div>
</main>
</template>

View File

@ -0,0 +1,26 @@
<script setup lang="ts">
import type { DockviewApi, DockviewPanelApi } from 'dockview-core';
import { ref, watch } from 'vue';
const props = defineProps<{api: DockviewPanelApi, containerApi: DockviewApi, params:{ time:number}}>();
console.log(props.api, props.containerApi, props.params);
const params = ref(props.params)
watch(() => props.params,(a,b) => {
console.log("watch",a,b);
params.value = b
})
</script>
<template>
<main>
<div>hi</div>
<div>{{ params.time }}</div>
</main>
</template>

View File

@ -0,0 +1,12 @@
<script setup lang="ts">
import type { DockviewApi, DockviewPanelApi } from 'dockview-core';
const props = defineProps<{api: DockviewPanelApi, containerApi: DockviewApi, params:{ time:number}}>();
</script>
<template>
<div style="width:100px;height:100%">
<div>hi</div>
</div>
</template>

View File

@ -0,0 +1,9 @@
<script setup lang="ts">
import TheWelcome from '../components/TheWelcome.vue'
</script>
<template>
<main>
<TheWelcome />
</main>
</template>

View File

@ -0,0 +1,20 @@
<script setup lang="ts">
import TestComponent from './testComponent.vue';
</script>
<template>
<div class="about">
<h1>This is a test page</h1>
<TestComponent/>
</div>
</template>
<style>
@media (min-width: 1024px) {
.about {
min-height: 100vh;
display: flex;
align-items: center;
}
}
</style>

View File

@ -0,0 +1,70 @@
<script>
import { defineComponent,ref } from 'vue'
// import DockviewVue from '../dockview-vue/dockviewVue';
import {DockviewVue} from 'dockview-vue';
import DefaultPanel from "./DefaultPanel.vue";
import AnotherPanel from "./AnotherPanel.vue"
import HeaderPanel from './HeaderPanel.vue';
// import {AgGridVue} from "ag-grid-vue3"
import "dockview-core/dist/styles/dockview.css";
export default defineComponent({
// type inference enabled
props: {
// name: String,
// msg: { type: String, required: true }
},
// data() {
// return {
// // valueA: "something"
// }
// },
components: {
"dockview-vue": DockviewVue
},
methods: {
onReady: (params) => {
console.log('api ready',params);
const panel1 = params.api.addPanel({
id:'panel_1',
component:"default"
});
setInterval(() => {
panel1.api.updateParameters({time: Date.now()});
}, 5000)
params.api.addPanel({
id:'panel_2',
component:"default"
});
}
},
setup(props,ctx) {
return {
components: {default:DefaultPanel, another:AnotherPanel},
headerPanel:HeaderPanel
}
},
})
</script>
<template>
<div>hello world</div>
<div>{{ valueA }}</div>
<dockview-vue
style="width:100%; height:100%"
class="dockview-theme-abyss"
:components="components"
:rightHeaderActionsComponent="headerPanel"
@ready="onReady"
/>
</template>

View File

@ -0,0 +1,14 @@
{
"extends": "@vue/tsconfig/tsconfig.dom.json",
"include": ["env.d.ts", "src/**/*", "src/**/*.vue"],
"exclude": ["src/**/__tests__/*"],
"compilerOptions": {
"composite": true,
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
}
}

View File

@ -0,0 +1,14 @@
{
"files": [],
"references": [
{
"path": "./tsconfig.node.json"
},
{
"path": "./tsconfig.app.json"
},
{
"path": "./tsconfig.vitest.json"
}
]
}

View File

@ -0,0 +1,19 @@
{
"extends": "@tsconfig/node20/tsconfig.json",
"include": [
"vite.config.*",
"vitest.config.*",
"cypress.config.*",
"nightwatch.conf.*",
"playwright.config.*"
],
"compilerOptions": {
"composite": true,
"noEmit": true,
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
"module": "ESNext",
"moduleResolution": "Bundler",
"types": ["node"]
}
}

View File

@ -0,0 +1,11 @@
{
"extends": "./tsconfig.app.json",
"exclude": [],
"compilerOptions": {
"composite": true,
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.vitest.tsbuildinfo",
"lib": [],
"types": ["node", "jsdom"]
}
}

View File

@ -0,0 +1,18 @@
import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import vueJsx from '@vitejs/plugin-vue-jsx'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
vue(),
vueJsx(),
],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
}
})

View File

@ -0,0 +1,14 @@
import { fileURLToPath } from 'node:url'
import { mergeConfig, defineConfig, configDefaults } from 'vitest/config'
import viteConfig from './vite.config'
export default mergeConfig(
viteConfig,
defineConfig({
test: {
environment: 'jsdom',
exclude: [...configDefaults.exclude, 'e2e/*'],
root: fileURLToPath(new URL('./', import.meta.url))
}
})
)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,56 @@
<div align="center">
<h1>dockview</h1>
<p>Zero dependency layout manager supporting tabs, groups, grids and splitviews with ReactJS support written in TypeScript</p>
</div>
---
[![npm version](https://badge.fury.io/js/dockview.svg)](https://www.npmjs.com/package/dockview)
[![npm](https://img.shields.io/npm/dm/dockview)](https://www.npmjs.com/package/dockview)
[![CI Build](https://github.com/mathuo/dockview/workflows/CI/badge.svg)](https://github.com/mathuo/dockview/actions?query=workflow%3ACI)
[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=mathuo_dockview&metric=coverage)](https://sonarcloud.io/summary/overall?id=mathuo_dockview)
[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=mathuo_dockview&metric=alert_status)](https://sonarcloud.io/summary/overall?id=mathuo_dockview)
[![Bundle Phobia](https://badgen.net/bundlephobia/minzip/dockview)](https://bundlephobia.com/result?p=dockview)
##
Please see the website: https://dockview.dev
## Features
- Serialization / deserialization with full layout management
- Support for split-views, grid-views and 'dockable' views
- Themeable and customizable
- Tab and Group docking / Drag n' Drop
- Popout Windows
- Floating Groups
- Extensive API
- Supports Shadow DOMs
- High test coverage
- Documentation website with live examples
- Transparent builds and Code Analysis
- Security at mind - verifed publishing and builds through GitHub Actions
Want to verify our builds? Go [here](https://www.npmjs.com/package/dockview#Provenance).
## Quick start
Dockview has a peer dependency on `react >= 16.8.0` and `react-dom >= 16.8.0`. You can install dockview from [npm](https://www.npmjs.com/package/dockview).
```
npm install --save dockview
```
Within your project you must import or reference the stylesheet at `dockview/dist/styles/dockview.css` and attach a theme.
```css
@import '~dockview/dist/styles/dockview.css';
```
You should also attach a dockview theme to an element containing your components. For example:
```html
<body classname="dockview-theme-dark"></body>
```

View File

@ -0,0 +1,6 @@
const gulp = require('gulp');
const buildfile = require('../../scripts/build');
buildfile.init();
gulp.task('run', gulp.series(['sass']));

View File

@ -0,0 +1,34 @@
import { JestConfigWithTsJest } from 'ts-jest';
const config: JestConfigWithTsJest = {
preset: 'ts-jest',
roots: ['<rootDir>/packages/dockview-react'],
modulePaths: ['<rootDir>/packages/dockview-react/src'],
displayName: { name: 'dockview', color: 'blue' },
rootDir: '../../',
collectCoverageFrom: [
'<rootDir>/packages/dockview-react/src/**/*.{js,jsx,ts,tsx}',
],
setupFiles: [
// '<rootDir>/packages/dockview-react/src/__tests__/__mocks__/resizeObserver.js',
],
setupFilesAfterEnv: ['<rootDir>/jest-setup.ts'],
coveragePathIgnorePatterns: ['/node_modules/'],
modulePathIgnorePatterns: [
// '<rootDir>/packages/dockview-react/src/__tests__/__mocks__',
// '<rootDir>/packages/dockview-react/src/__tests__/__test_utils__',
],
coverageDirectory: '<rootDir>/packages/dockview-react/coverage/',
testResultsProcessor: 'jest-sonar-reporter',
testEnvironment: 'jsdom',
transform: {
'^.+\\.tsx?$': [
'ts-jest',
{
tsconfig: '<rootDir>/tsconfig.test.json',
},
],
},
};
export default config;

View File

@ -0,0 +1,59 @@
{
"name": "dockview-react",
"version": "0.0.0-beta-0",
"description": "Zero dependency layout manager supporting tabs, grids and splitviews with ReactJS support",
"keywords": [
"splitview",
"split-view",
"gridview",
"grid-view",
"dockview",
"dock-view",
"grid",
"tabs",
"layout",
"layout manager",
"dock layout",
"dock",
"docking",
"splitter",
"drag-and-drop",
"drag",
"drop",
"react",
"react-component"
],
"homepage": "https://github.com/mathuo/dockview",
"bugs": {
"url": "https://github.com/mathuo/dockview/issues"
},
"repository": {
"type": "git",
"url": "https://github.com/mathuo/dockview.git"
},
"license": "MIT",
"author": "https://github.com/mathuo",
"main": "./dist/cjs/index.js",
"module": "./dist/esm/index.js",
"types": "./dist/cjs/index.d.ts",
"files": [
"dist",
"README.md"
],
"scripts": {
"build": "npm run build:package && npm run build:bundles",
"build:bundles": "rollup -c",
"build:cjs": "cross-env ../../node_modules/.bin/tsc --build ./tsconfig.json --verbose --extendedDiagnostics",
"build:css": "gulp sass",
"build:esm": "cross-env ../../node_modules/.bin/tsc --build ./tsconfig.esm.json --verbose --extendedDiagnostics",
"build:package": "npm run build:cjs && npm run build:esm && npm run build:css",
"clean": "rimraf dist/ .build/ .rollup.cache/",
"prepublishOnly": "npm run rebuild && npm run test",
"rebuild": "npm run clean && npm run build",
"test": "cross-env ../../node_modules/.bin/jest --selectProjects dockview",
"test:cov": "cross-env ../../node_modules/.bin/jest --selectProjects dockview --coverage"
},
"dependencies": {
"dockview": "^1.10.1"
}
}

View File

@ -0,0 +1,113 @@
/* eslint-disable */
const { join } = require('path');
const typescript = require('@rollup/plugin-typescript');
const terser = require('@rollup/plugin-terser');
const postcss = require('rollup-plugin-postcss');
const nodeResolve = require('@rollup/plugin-node-resolve');
const { name, version, homepage, license } = require('./package.json');
const main = join(__dirname, './scripts/rollupEntryTarget.ts');
const mainNoStyles = join(__dirname, './src/index.ts');
const outputDir = join(__dirname, 'dist');
function outputFile(format, isMinified, withStyles) {
let filename = join(outputDir, name);
if (format !== 'umd') {
filename += `.${format}`;
}
if (isMinified) {
filename += '.min';
}
if (!withStyles) {
filename += '.noStyle';
}
return `${filename}.js`;
}
function getInput(options) {
const { withStyles } = options;
if (withStyles) {
return main;
}
return mainNoStyles;
}
function createBundle(format, options) {
const { withStyles, isMinified } = options;
const input = getInput(options);
const file = outputFile(format, isMinified, withStyles);
const external = [];
const output = {
file,
format,
sourcemap: true,
globals: {},
banner: [
`/**`,
` * ${name}`,
` * @version ${version}`,
` * @link ${homepage}`,
` * @license ${license}`,
` */`,
].join('\n'),
};
const plugins = [
nodeResolve({
include: ['node_modules/dockview-core/**'],
}),
typescript({
tsconfig: 'tsconfig.esm.json',
}),
];
if (isMinified) {
plugins.push(terser());
}
if (withStyles) {
plugins.push(postcss());
}
if (format === 'umd') {
output['name'] = name;
}
external.push('react', 'react-dom');
if (format === 'umd') {
output.globals['react'] = 'React';
output.globals['react-dom'] = 'ReactDOM';
}
return {
input,
output,
plugins,
external,
};
}
module.exports = [
// amd
createBundle('amd', { withStyles: false, isMinified: false }),
createBundle('amd', { withStyles: true, isMinified: false }),
createBundle('amd', { withStyles: false, isMinified: true }),
createBundle('amd', { withStyles: true, isMinified: true }),
// umd
createBundle('umd', { withStyles: false, isMinified: false }),
createBundle('umd', { withStyles: true, isMinified: false }),
createBundle('umd', { withStyles: false, isMinified: true }),
createBundle('umd', { withStyles: true, isMinified: true }),
// cjs
createBundle('cjs', { withStyles: true, isMinified: false }),
// esm
createBundle('esm', { withStyles: true, isMinified: false }),
createBundle('esm', { withStyles: true, isMinified: true }),
];

View File

@ -0,0 +1,2 @@
import '../dist/styles/dockview.css';
export * from '../src/index';

View File

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

View File

View File

@ -0,0 +1 @@
export * from 'dockview';

View File

@ -0,0 +1,14 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"module": "ES2020",
"moduleResolution": "node",
"target": "es6",
"outDir": "dist/esm",
"tsBuildInfoFile": ".build/tsconfig.tsbuildinfo.esm",
"jsx": "react",
"rootDir": "src"
},
"include": ["src"],
"exclude": ["**/node_modules", "src/__tests__"]
}

View File

@ -0,0 +1,11 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"outDir": "dist/cjs",
"tsBuildInfoFile": ".build/tsconfig.tsbuildinfo.cjs",
"jsx": "react",
"rootDir": "src"
},
"include": ["src"],
"exclude": ["**/node_modules", "src/__tests__"]
}

View File

@ -0,0 +1,5 @@
{
"extends": ["../../typedoc.base.json"],
"entryPoints": ["src/index.ts"],
"exclude": ["**/dist/**"]
}

View File

@ -0,0 +1,56 @@
<div align="center">
<h1>dockview</h1>
<p>Zero dependency layout manager supporting tabs, groups, grids and splitviews with ReactJS support written in TypeScript</p>
</div>
---
[![npm version](https://badge.fury.io/js/dockview.svg)](https://www.npmjs.com/package/dockview)
[![npm](https://img.shields.io/npm/dm/dockview)](https://www.npmjs.com/package/dockview)
[![CI Build](https://github.com/mathuo/dockview/workflows/CI/badge.svg)](https://github.com/mathuo/dockview/actions?query=workflow%3ACI)
[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=mathuo_dockview&metric=coverage)](https://sonarcloud.io/summary/overall?id=mathuo_dockview)
[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=mathuo_dockview&metric=alert_status)](https://sonarcloud.io/summary/overall?id=mathuo_dockview)
[![Bundle Phobia](https://badgen.net/bundlephobia/minzip/dockview)](https://bundlephobia.com/result?p=dockview)
##
Please see the website: https://dockview.dev
## Features
- Serialization / deserialization with full layout management
- Support for split-views, grid-views and 'dockable' views
- Themeable and customizable
- Tab and Group docking / Drag n' Drop
- Popout Windows
- Floating Groups
- Extensive API
- Supports Shadow DOMs
- High test coverage
- Documentation website with live examples
- Transparent builds and Code Analysis
- Security at mind - verifed publishing and builds through GitHub Actions
Want to verify our builds? Go [here](https://www.npmjs.com/package/dockview#Provenance).
## Quick start
Dockview has a peer dependency on `react >= 16.8.0` and `react-dom >= 16.8.0`. You can install dockview from [npm](https://www.npmjs.com/package/dockview).
```
npm install --save dockview
```
Within your project you must import or reference the stylesheet at `dockview/dist/styles/dockview.css` and attach a theme.
```css
@import '~dockview/dist/styles/dockview.css';
```
You should also attach a dockview theme to an element containing your components. For example:
```html
<body classname="dockview-theme-dark"></body>
```

View File

@ -0,0 +1,6 @@
const gulp = require('gulp');
const buildfile = require('../../scripts/build');
buildfile.init();
gulp.task('run', gulp.series(['sass']));

View File

@ -0,0 +1,34 @@
import { JestConfigWithTsJest } from 'ts-jest';
const config: JestConfigWithTsJest = {
preset: 'ts-jest',
roots: ['<rootDir>/packages/dockview-vue'],
modulePaths: ['<rootDir>/packages/dockview-vue/src'],
displayName: { name: 'dockview', color: 'blue' },
rootDir: '../../',
collectCoverageFrom: [
'<rootDir>/packages/dockview-vue/src/**/*.{js,jsx,ts,tsx}',
],
setupFiles: [
// '<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__',
],
coverageDirectory: '<rootDir>/packages/dockview-vue/coverage/',
testResultsProcessor: 'jest-sonar-reporter',
testEnvironment: 'jsdom',
transform: {
'^.+\\.tsx?$': [
'ts-jest',
{
tsconfig: '<rootDir>/tsconfig.test.json',
},
],
},
};
export default config;

View File

@ -0,0 +1,61 @@
{
"name": "dockview-vue",
"version": "0.0.0-beta-0",
"description": "Zero dependency layout manager supporting tabs, grids and splitviews with ReactJS support",
"keywords": [
"splitview",
"split-view",
"gridview",
"grid-view",
"dockview",
"dock-view",
"grid",
"tabs",
"layout",
"layout manager",
"dock layout",
"dock",
"docking",
"splitter",
"drag-and-drop",
"drag",
"drop",
"react",
"react-component"
],
"main": "dist/dockview-vue.umd.js",
"module": "dist/dockview-vue.es.js",
"types": "dist/types/index.d.ts",
"homepage": "https://github.com/mathuo/dockview",
"bugs": {
"url": "https://github.com/mathuo/dockview/issues"
},
"repository": {
"type": "git",
"url": "https://github.com/mathuo/dockview.git"
},
"license": "MIT",
"author": "https://github.com/mathuo",
"files": [
"dist",
"README.md"
],
"scripts": {
"build-only": "vite build",
"build": "npm run build:package && npm run build:bundles",
"build:bundles": "rollup -c",
"build:cjs": "cross-env ../../node_modules/.bin/vue-tsc --build ./tsconfig.json --verbose --extendedDiagnostics",
"build:css": "gulp sass",
"build:esm": "cross-env ../../node_modules/.bin/vue-tsc --build ./tsconfig.esm.json --verbose --extendedDiagnostics",
"build:package": "npm run build-only && npm run build:types",
"clean": "rimraf dist/ .build/ .rollup.cache/",
"prepublishOnly": "npm run rebuild && npm run test",
"rebuild": "npm run clean && npm run build",
"test": "cross-env ../../node_modules/.bin/jest --selectProjects dockview",
"test:cov": "cross-env ../../node_modules/.bin/jest --selectProjects dockview --coverage",
"build:types": "vue-tsc --project tsconfig.build-types.json --declaration --emitDeclarationOnly --outDir dist/types "
},
"dependencies": {
"dockview-core": "^1.10.1"
}
}

View File

@ -0,0 +1,113 @@
/* eslint-disable */
const { join } = require('path');
const typescript = require('@rollup/plugin-typescript');
const terser = require('@rollup/plugin-terser');
const postcss = require('rollup-plugin-postcss');
const nodeResolve = require('@rollup/plugin-node-resolve');
const { name, version, homepage, license } = require('./package.json');
const main = join(__dirname, './scripts/rollupEntryTarget.ts');
const mainNoStyles = join(__dirname, './src/index.ts');
const outputDir = join(__dirname, 'dist');
function outputFile(format, isMinified, withStyles) {
let filename = join(outputDir, name);
if (format !== 'umd') {
filename += `.${format}`;
}
if (isMinified) {
filename += '.min';
}
if (!withStyles) {
filename += '.noStyle';
}
return `${filename}.js`;
}
function getInput(options) {
const { withStyles } = options;
if (withStyles) {
return main;
}
return mainNoStyles;
}
function createBundle(format, options) {
const { withStyles, isMinified } = options;
const input = getInput(options);
const file = outputFile(format, isMinified, withStyles);
const external = [];
const output = {
file,
format,
sourcemap: true,
globals: {},
banner: [
`/**`,
` * ${name}`,
` * @version ${version}`,
` * @link ${homepage}`,
` * @license ${license}`,
` */`,
].join('\n'),
};
const plugins = [
nodeResolve({
include: ['node_modules/dockview-core/**'],
}),
typescript({
tsconfig: 'tsconfig.esm.json',
}),
];
if (isMinified) {
plugins.push(terser());
}
if (withStyles) {
plugins.push(postcss());
}
if (format === 'umd') {
output['name'] = name;
}
external.push('react', 'react-dom');
if (format === 'umd') {
output.globals['react'] = 'React';
output.globals['react-dom'] = 'ReactDOM';
}
return {
input,
output,
plugins,
external,
};
}
module.exports = [
// amd
createBundle('amd', { withStyles: false, isMinified: false }),
createBundle('amd', { withStyles: true, isMinified: false }),
createBundle('amd', { withStyles: false, isMinified: true }),
createBundle('amd', { withStyles: true, isMinified: true }),
// umd
createBundle('umd', { withStyles: false, isMinified: false }),
createBundle('umd', { withStyles: true, isMinified: false }),
createBundle('umd', { withStyles: false, isMinified: true }),
createBundle('umd', { withStyles: true, isMinified: true }),
// cjs
createBundle('cjs', { withStyles: true, isMinified: false }),
// esm
createBundle('esm', { withStyles: true, isMinified: false }),
createBundle('esm', { withStyles: true, isMinified: true }),
];

View File

@ -0,0 +1,2 @@
import '../dist/styles/dockview.css';
export * from '../src/index';

View File

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

View File

@ -0,0 +1,285 @@
<script setup lang="ts">
import {
DockviewApi,
DockviewComponent,
type IContentRenderer,
type ITabRenderer,
type IWatermarkRenderer,
type IDockviewPanelProps,
type IDockviewPanelHeaderProps,
type IGroupPanelBaseProps,
type IWatermarkPanelProps,
type DockviewOptions,
PROPERTY_KEYS,
type DockviewFrameworkOptions,
type DockviewReadyEvent,
} from 'dockview-core';
import {
ref,
onMounted,
defineProps,
defineEmits,
watch,
onBeforeUnmount,
} from 'vue';
import {
VueContentRenderer,
VueHeaderActionsRenderer,
VueTabRenderer,
VueWatermarkRenderer,
type VueComponent,
} from '../utils';
interface VueProps {
components: Record<string, VueComponent<IDockviewPanelProps>>;
tabComponents?: Record<string, VueComponent<IDockviewPanelHeaderProps>>;
watermarkComponent?: VueComponent<IWatermarkPanelProps>;
defaultTabComponent?: VueComponent<IDockviewPanelHeaderProps>;
rightHeaderActionsComponent?: VueComponent<IGroupPanelBaseProps>;
leftHeaderActionsComponent?: VueComponent<IGroupPanelBaseProps>;
prefixHeaderActionsComponent?: VueComponent<IGroupPanelBaseProps>;
}
const VUE_PROPERTIES = (() => {
const _value: Record<keyof VueProps, undefined> = {
components: undefined,
tabComponents: undefined,
watermarkComponent: undefined,
defaultTabComponent: undefined,
rightHeaderActionsComponent: undefined,
leftHeaderActionsComponent: undefined,
prefixHeaderActionsComponent: undefined,
};
return Object.keys(_value) as (keyof VueProps)[];
})();
type VueEvents = {
ready: [event: DockviewReadyEvent];
};
const DEFAULT_REACT_TAB = 'props.defaultTabComponent';
export type IDockviewVueProps = DockviewOptions & VueProps;
function extractCoreOptions(props: IDockviewVueProps): DockviewOptions {
const coreOptions = (PROPERTY_KEYS as (keyof DockviewOptions)[]).reduce(
(obj, key) => {
(obj as any)[key] = props[key];
return obj;
},
{} as Partial<DockviewOptions>
);
return coreOptions as DockviewOptions;
}
const emit = defineEmits<VueEvents>();
const props = defineProps<IDockviewVueProps>();
const el = ref<HTMLElement | null>(null);
const instance = ref<DockviewComponent | null>(null);
PROPERTY_KEYS.forEach((coreOptionKey) => {
watch(
() => props[coreOptionKey],
(newValue, oldValue) => {
if (instance.value) {
instance.value.updateOptions({ [coreOptionKey]: newValue });
}
}
);
});
watch(
() => props.components,
(newValue, oldValue) => {
if (instance.value) {
instance.value.updateOptions({ frameworkComponents: newValue });
}
}
);
watch(
() => [props.tabComponents, props.defaultTabComponent],
([newTabComponents, newDefaultTabComponent], oldValue) => {
if (instance.value) {
const frameworkTabComponents = newTabComponents ?? {};
if (newDefaultTabComponent) {
frameworkTabComponents[DEFAULT_REACT_TAB] =
newDefaultTabComponent;
}
instance.value.updateOptions({
defaultTabComponent: newDefaultTabComponent
? DEFAULT_REACT_TAB
: undefined,
frameworkTabComponents,
});
}
}
);
watch(
() => props.watermarkComponent,
(newValue, oldValue) => {
if (instance.value) {
instance.value.updateOptions({
watermarkFrameworkComponent: newValue,
});
}
}
);
watch(
() => props.leftHeaderActionsComponent,
(newValue, oldValue) => {
if (instance.value) {
instance.value.updateOptions({
headerLeftActionComponent: newValue
? (group) => {
return new VueHeaderActionsRenderer(
newValue as VueComponent,
group
);
}
: undefined,
});
}
}
);
watch(
() => props.rightHeaderActionsComponent,
(newValue, oldValue) => {
if (instance.value) {
instance.value.updateOptions({
headerRightActionComponent: newValue
? (group) => {
return new VueHeaderActionsRenderer(
newValue as VueComponent,
group
);
}
: undefined,
});
}
}
);
watch(
() => props.prefixHeaderActionsComponent,
(newValue, oldValue) => {
if (instance.value) {
instance.value.updateOptions({
headerPrefixActionComponent: newValue
? (group) => {
return new VueHeaderActionsRenderer(
newValue as VueComponent,
group
);
}
: undefined,
});
}
}
);
onMounted(() => {
if (!el.value) {
throw new Error('element is not mounted');
}
const frameworkTabComponents = props.tabComponents ?? {};
if (props.defaultTabComponent) {
frameworkTabComponents[DEFAULT_REACT_TAB] = props.defaultTabComponent;
}
const frameworkOptions: DockviewFrameworkOptions = {
parentElement: el.value,
frameworkComponentFactory: {
content: {
createComponent: (
id: string,
componentId: string,
component: any
): IContentRenderer => {
return new VueContentRenderer(component);
},
},
tab: {
createComponent: (
id: string,
componentId: string,
component: any
): ITabRenderer => {
return new VueTabRenderer(component);
},
},
watermark: {
createComponent: (
id: string,
componentId: string,
component: any
): IWatermarkRenderer => {
return new VueWatermarkRenderer(component);
},
},
// action: {
// createComponent: (id: string, componentId: string, component: any): IWatermarkRenderer => {
// return new VueHeaderActionRenderer(component)
// }
// }
},
frameworkComponents: props.components,
frameworkTabComponents,
headerLeftActionComponent: props.leftHeaderActionsComponent
? (group) => {
return new VueHeaderActionsRenderer(
props.leftHeaderActionsComponent as VueComponent,
group
);
}
: undefined,
headerPrefixActionComponent: props.prefixHeaderActionsComponent
? (group) => {
return new VueHeaderActionsRenderer(
props.prefixHeaderActionsComponent as VueComponent,
group
);
}
: undefined,
headerRightActionComponent: props.rightHeaderActionsComponent
? (group) => {
return new VueHeaderActionsRenderer(
props.rightHeaderActionsComponent as VueComponent,
group
);
}
: undefined,
defaultTabComponent: props.defaultTabComponent
? DEFAULT_REACT_TAB
: undefined,
};
const dockview = new DockviewComponent({
...extractCoreOptions(props),
...frameworkOptions,
});
instance.value = dockview;
emit('ready', { api: new DockviewApi(dockview) });
});
onBeforeUnmount(() => {
if (instance.value) {
instance.value.dispose();
}
});
</script>
<template>
<div ref="el" />
</template>

View File

View File

@ -0,0 +1,5 @@
export * from 'dockview-core';
import DockviewVue from './dockview/dockview.vue';
export { DockviewVue };
export * from './dockview/dockview.vue';

View File

@ -0,0 +1,236 @@
import type {
DockviewApi,
DockviewGroupPanel,
DockviewGroupPanelApi,
GroupPanelPartInitParameters,
IContentRenderer,
IHeaderActionsRenderer,
ITabRenderer,
IWatermarkRenderer,
PanelUpdateEvent,
Parameters,
WatermarkRendererInitParameters,
} from 'dockview-core';
import {
createVNode,
type ComponentOptionsBase,
render,
cloneVNode,
mergeProps,
type DefineComponent,
} from 'vue';
export type ComponentInterface = ComponentOptionsBase<
any,
any,
any,
any,
any,
any,
any,
any
>;
export type VueComponent<T = any> = DefineComponent<T>;
/**
* TODO List
*
* 1. handle vue context-ish stuff (appContext? provides?)
*
*
*
* @see https://vuejs.org/api/render-function.html#clonevnode
* @see https://vuejs.org/api/render-function.html#mergeprops
*/
export function mountVueComponent<T extends Record<string, any>>(
component: VueComponent<T>,
props: T,
element: HTMLElement
) {
let vNode = createVNode(component, props);
render(vNode, element);
return {
update: (newProps: any) => {
vNode = cloneVNode(vNode, mergeProps(props, newProps));
render(vNode, element);
},
dispose: () => {
render(null, element);
},
};
}
export class VueContentRenderer implements IContentRenderer {
private _element: HTMLElement;
private _renderDisposable:
| { update: (props: any) => void; dispose: () => void }
| undefined;
get element(): HTMLElement {
return this._element;
}
constructor(private readonly component: VueComponent) {
this._element = document.createElement('div');
this.element.className = 'dv-vue-part';
}
init(parameters: GroupPanelPartInitParameters): void {
const props = {
params: parameters.params,
api: parameters.api,
containerApi: parameters.containerApi,
};
this._renderDisposable?.dispose();
this._renderDisposable = mountVueComponent(
this.component,
props,
this.element
);
}
update(event: PanelUpdateEvent<Parameters>): void {
const params = event.params;
// TODO: handle prop updates somehow?
this._renderDisposable?.update(params);
}
focus(): void {
// TODO: make optional on interface
}
dispose(): void {
this._renderDisposable?.dispose();
}
}
export class VueTabRenderer implements ITabRenderer {
private _element: HTMLElement;
private _renderDisposable:
| { update: (props: any) => void; dispose: () => void }
| undefined;
get element(): HTMLElement {
return this._element;
}
constructor(private readonly component: VueComponent) {
this._element = document.createElement('div');
this.element.className = 'dv-vue-part';
}
init(parameters: GroupPanelPartInitParameters): void {
const props = {
params: parameters.params,
api: parameters.api,
containerApi: parameters.containerApi,
};
this._renderDisposable?.dispose();
this._renderDisposable = mountVueComponent(
this.component,
props,
this.element
);
}
update(event: PanelUpdateEvent<Parameters>): void {
const params = event.params;
// TODO: handle prop updates somehow?
this._renderDisposable?.update(params);
}
dispose(): void {
this._renderDisposable?.dispose();
}
}
export class VueWatermarkRenderer implements IWatermarkRenderer {
private _element: HTMLElement;
private _renderDisposable:
| { update: (props: any) => void; dispose: () => void }
| undefined;
get element(): HTMLElement {
return this._element;
}
constructor(private readonly component: VueComponent) {
this._element = document.createElement('div');
this.element.className = 'dv-vue-part';
}
init(parameters: WatermarkRendererInitParameters): void {
const props = {
group: parameters.group,
containerApi: parameters.containerApi,
};
this._renderDisposable?.dispose();
this._renderDisposable = mountVueComponent(
this.component,
props,
this.element
);
}
updateParentGroup(group: DockviewGroupPanel, visible: boolean): void {
// TODO: make optional on interface
}
update(event: PanelUpdateEvent<Parameters>): void {
const params = event.params;
// TODO: handle prop updates somehow?
this._renderDisposable?.update(params);
}
dispose(): void {
this._renderDisposable?.dispose();
}
}
export class VueHeaderActionsRenderer implements IHeaderActionsRenderer {
private _element: HTMLElement;
private _renderDisposable:
| { update: (props: any) => void; dispose: () => void }
| undefined;
get element(): HTMLElement {
return this._element;
}
constructor(
private readonly component: VueComponent,
group: DockviewGroupPanel
) {
this._element = document.createElement('div');
this.element.className = 'dv-vue-header-action-part';
}
init(params: {
containerApi: DockviewApi;
api: DockviewGroupPanelApi;
}): void {
console.log('meeee', this.component);
const props = {
api: params.api,
containerApi: params.containerApi,
};
this._renderDisposable?.dispose();
this._renderDisposable = mountVueComponent(
this.component,
props,
this.element
);
}
dispose(): void {
console.log('dispose');
this._renderDisposable?.dispose();
}
}

View File

@ -0,0 +1,10 @@
// tsconfig.app.json
{
"extends": "@vue/tsconfig/tsconfig.json",
"include": ["env.d.ts", "src/**/*", "src/**/*.vue"],
"exclude": ["src/**/__tests__/*", "src/**/*.cy.ts"],
"compilerOptions": {
"composite": true,
"baseUrl": "."
}
}

View File

@ -0,0 +1,10 @@
// tsconfig.build-types.json
{
"include": ["src/**/*"],
"exclude": [
"node_modules",
"**/*.cy.ts",
"**/*.spec.ts",
"**/__tests__/**/*"
]
}

View File

@ -0,0 +1,9 @@
// tsconfig.config.json
{
"extends": "@vue/tsconfig/tsconfig.json",
"include": ["vite.config.*", "vitest.config.*", "cypress.config.*"],
"compilerOptions": {
"composite": true,
"types": ["node"]
}
}

View File

@ -0,0 +1,5 @@
// tsconfig.json
{
"files": [],
"references": [{ "path": "./tsconfig.config.json" }, { "path": "./tsconfig.app.json" }]
}

View File

@ -0,0 +1,5 @@
{
"extends": ["../../typedoc.base.json"],
"entryPoints": ["src/index.ts"],
"exclude": ["**/dist/**"]
}

Some files were not shown because too many files have changed in this diff Show More