mirror of
https://github.com/mathuo/dockview
synced 2025-02-01 22:15:44 +00:00
feat: Vue 3 Support
This commit is contained in:
parent
45919ff397
commit
c5770d4381
3
.vscode/extensions.json
vendored
3
.vscode/extensions.json
vendored
@ -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": []
|
||||||
|
12
package.json
12
package.json
@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
56
packages/dockview-angular/README.md
Normal file
56
packages/dockview-angular/README.md
Normal 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>
|
||||||
|
```
|
6
packages/dockview-angular/gulpfile.js
Normal file
6
packages/dockview-angular/gulpfile.js
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
const gulp = require('gulp');
|
||||||
|
const buildfile = require('../../scripts/build');
|
||||||
|
|
||||||
|
buildfile.init();
|
||||||
|
|
||||||
|
gulp.task('run', gulp.series(['sass']));
|
34
packages/dockview-angular/jest.config.ts
Normal file
34
packages/dockview-angular/jest.config.ts
Normal 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;
|
59
packages/dockview-angular/package.json
Normal file
59
packages/dockview-angular/package.json
Normal 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"
|
||||||
|
}
|
||||||
|
}
|
113
packages/dockview-angular/rollup.config.js
Normal file
113
packages/dockview-angular/rollup.config.js
Normal 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 }),
|
||||||
|
];
|
2
packages/dockview-angular/scripts/rollupEntryTarget.ts
Normal file
2
packages/dockview-angular/scripts/rollupEntryTarget.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
import '../dist/styles/dockview.css';
|
||||||
|
export * from '../src/index';
|
5
packages/dockview-angular/src/__tests__/empty.spec.ts
Normal file
5
packages/dockview-angular/src/__tests__/empty.spec.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
describe('empty', () => {
|
||||||
|
test('that passes', () => {
|
||||||
|
expect(true).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
121
packages/dockview-angular/src/dockview.vue
Normal file
121
packages/dockview-angular/src/dockview.vue
Normal 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>
|
0
packages/dockview-angular/src/index.scss
Normal file
0
packages/dockview-angular/src/index.scss
Normal file
1
packages/dockview-angular/src/index.ts
Normal file
1
packages/dockview-angular/src/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from 'dockview-core';
|
151
packages/dockview-angular/src/utils.ts
Normal file
151
packages/dockview-angular/src/utils.ts
Normal 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()
|
||||||
|
}
|
||||||
|
}
|
14
packages/dockview-angular/tsconfig.esm.json
Normal file
14
packages/dockview-angular/tsconfig.esm.json
Normal 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__"]
|
||||||
|
}
|
11
packages/dockview-angular/tsconfig.json
Normal file
11
packages/dockview-angular/tsconfig.json
Normal 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__"]
|
||||||
|
}
|
5
packages/dockview-angular/typedoc.json
Normal file
5
packages/dockview-angular/typedoc.json
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"extends": ["../../typedoc.base.json"],
|
||||||
|
"entryPoints": ["src/index.ts"],
|
||||||
|
"exclude": ["**/dist/**"]
|
||||||
|
}
|
@ -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();
|
||||||
});
|
});
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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.
|
||||||
*/
|
*/
|
||||||
|
@ -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) {
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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,25 +535,25 @@ 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
|
* the center position so that external drag events can fall through to the group
|
||||||
* the center position so that external drag events can fall through to the group
|
* and panel drop target handlers
|
||||||
* and panel drop target handlers
|
*/
|
||||||
*/
|
return false;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.options.showDndOverlay({
|
|
||||||
nativeEvent: event,
|
|
||||||
position: position,
|
|
||||||
target: 'edge',
|
|
||||||
getData: getPanelData,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
const firedEvent = new DockviewUnhandledDragOverEvent(
|
||||||
|
event,
|
||||||
|
'edge',
|
||||||
|
position,
|
||||||
|
getPanelData
|
||||||
|
);
|
||||||
|
|
||||||
|
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;
|
||||||
|
@ -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({
|
target,
|
||||||
nativeEvent: event,
|
position,
|
||||||
target,
|
getPanelData,
|
||||||
group: this.accessor.getPanel(this.id)!,
|
this.accessor.getPanel(this.id)!
|
||||||
position,
|
);
|
||||||
getData: getPanelData,
|
|
||||||
});
|
this._onUnhandledDragOverEvent.fire(firedEvent);
|
||||||
}
|
|
||||||
return false;
|
return firedEvent.isAccepted;
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleDropEvent(
|
private handleDropEvent(
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
42
packages/dockview-core/src/dockview/framework.ts
Normal file
42
packages/dockview-core/src/dockview/framework.ts
Normal 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;
|
||||||
|
}
|
@ -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;
|
||||||
|
@ -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
|
||||||
|
6
packages/dockview-core/src/framwork.ts
Normal file
6
packages/dockview-core/src/framwork.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import { Parameters } from "./panel/types";
|
||||||
|
|
||||||
|
export interface PanelParameters<T extends {} = Parameters> {
|
||||||
|
params: T;
|
||||||
|
}
|
||||||
|
|
@ -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';
|
||||||
|
15
packages/dockview-examples/vue-project/.eslintrc.cjs
Normal file
15
packages/dockview-examples/vue-project/.eslintrc.cjs
Normal 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'
|
||||||
|
}
|
||||||
|
}
|
30
packages/dockview-examples/vue-project/.gitignore
vendored
Normal file
30
packages/dockview-examples/vue-project/.gitignore
vendored
Normal 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
|
8
packages/dockview-examples/vue-project/.prettierrc.json
Normal file
8
packages/dockview-examples/vue-project/.prettierrc.json
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://json.schemastore.org/prettierrc",
|
||||||
|
"semi": false,
|
||||||
|
"tabWidth": 2,
|
||||||
|
"singleQuote": true,
|
||||||
|
"printWidth": 100,
|
||||||
|
"trailingComma": "none"
|
||||||
|
}
|
8
packages/dockview-examples/vue-project/.vscode/extensions.json
vendored
Normal file
8
packages/dockview-examples/vue-project/.vscode/extensions.json
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"recommendations": [
|
||||||
|
"Vue.volar",
|
||||||
|
"Vue.vscode-typescript-vue-plugin",
|
||||||
|
"dbaeumer.vscode-eslint",
|
||||||
|
"esbenp.prettier-vscode"
|
||||||
|
]
|
||||||
|
}
|
52
packages/dockview-examples/vue-project/README.md
Normal file
52
packages/dockview-examples/vue-project/README.md
Normal 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
|
||||||
|
```
|
1
packages/dockview-examples/vue-project/env.d.ts
vendored
Normal file
1
packages/dockview-examples/vue-project/env.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
/// <reference types="vite/client" />
|
13
packages/dockview-examples/vue-project/index.html
Normal file
13
packages/dockview-examples/vue-project/index.html
Normal 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>
|
35
packages/dockview-examples/vue-project/package.json
Normal file
35
packages/dockview-examples/vue-project/package.json
Normal 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"
|
||||||
|
}
|
||||||
|
}
|
BIN
packages/dockview-examples/vue-project/public/favicon.ico
Normal file
BIN
packages/dockview-examples/vue-project/public/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.2 KiB |
87
packages/dockview-examples/vue-project/src/App.vue
Normal file
87
packages/dockview-examples/vue-project/src/App.vue
Normal 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>
|
86
packages/dockview-examples/vue-project/src/assets/base.css
Normal file
86
packages/dockview-examples/vue-project/src/assets/base.css
Normal 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;
|
||||||
|
}
|
@ -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 |
35
packages/dockview-examples/vue-project/src/assets/main.css
Normal file
35
packages/dockview-examples/vue-project/src/assets/main.css
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,41 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
defineProps<{
|
||||||
|
msg: string
|
||||||
|
}>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="greetings">
|
||||||
|
<h1 class="green">{{ msg }}</h1>
|
||||||
|
<h3>
|
||||||
|
You’ve 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>
|
@ -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>
|
||||||
|
|
||||||
|
Vue’s
|
||||||
|
<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>
|
@ -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>
|
@ -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')
|
||||||
|
})
|
||||||
|
})
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
151
packages/dockview-examples/vue-project/src/dockview-vue/utils.ts
Normal file
151
packages/dockview-examples/vue-project/src/dockview-vue/utils.ts
Normal 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()
|
||||||
|
}
|
||||||
|
}
|
14
packages/dockview-examples/vue-project/src/main.ts
Normal file
14
packages/dockview-examples/vue-project/src/main.ts
Normal 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')
|
23
packages/dockview-examples/vue-project/src/router/index.ts
Normal file
23
packages/dockview-examples/vue-project/src/router/index.ts
Normal 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
|
12
packages/dockview-examples/vue-project/src/stores/counter.ts
Normal file
12
packages/dockview-examples/vue-project/src/stores/counter.ts
Normal 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 }
|
||||||
|
})
|
@ -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>
|
@ -0,0 +1,10 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
|
||||||
|
defineEmits({});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<main>
|
||||||
|
<div>hi</div>
|
||||||
|
</main>
|
||||||
|
</template>
|
@ -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>
|
@ -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>
|
@ -0,0 +1,9 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import TheWelcome from '../components/TheWelcome.vue'
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<main>
|
||||||
|
<TheWelcome />
|
||||||
|
</main>
|
||||||
|
</template>
|
@ -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>
|
@ -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>
|
||||||
|
|
14
packages/dockview-examples/vue-project/tsconfig.app.json
Normal file
14
packages/dockview-examples/vue-project/tsconfig.app.json
Normal 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/*"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
14
packages/dockview-examples/vue-project/tsconfig.json
Normal file
14
packages/dockview-examples/vue-project/tsconfig.json
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"files": [],
|
||||||
|
"references": [
|
||||||
|
{
|
||||||
|
"path": "./tsconfig.node.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "./tsconfig.app.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "./tsconfig.vitest.json"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
19
packages/dockview-examples/vue-project/tsconfig.node.json
Normal file
19
packages/dockview-examples/vue-project/tsconfig.node.json
Normal 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"]
|
||||||
|
}
|
||||||
|
}
|
11
packages/dockview-examples/vue-project/tsconfig.vitest.json
Normal file
11
packages/dockview-examples/vue-project/tsconfig.vitest.json
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"extends": "./tsconfig.app.json",
|
||||||
|
"exclude": [],
|
||||||
|
"compilerOptions": {
|
||||||
|
"composite": true,
|
||||||
|
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.vitest.tsbuildinfo",
|
||||||
|
|
||||||
|
"lib": [],
|
||||||
|
"types": ["node", "jsdom"]
|
||||||
|
}
|
||||||
|
}
|
18
packages/dockview-examples/vue-project/vite.config.ts
Normal file
18
packages/dockview-examples/vue-project/vite.config.ts
Normal 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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
14
packages/dockview-examples/vue-project/vitest.config.ts
Normal file
14
packages/dockview-examples/vue-project/vitest.config.ts
Normal 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))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
2128
packages/dockview-examples/vue-project/yarn.lock
Normal file
2128
packages/dockview-examples/vue-project/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
56
packages/dockview-react/README.md
Normal file
56
packages/dockview-react/README.md
Normal 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>
|
||||||
|
```
|
6
packages/dockview-react/gulpfile.js
Normal file
6
packages/dockview-react/gulpfile.js
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
const gulp = require('gulp');
|
||||||
|
const buildfile = require('../../scripts/build');
|
||||||
|
|
||||||
|
buildfile.init();
|
||||||
|
|
||||||
|
gulp.task('run', gulp.series(['sass']));
|
34
packages/dockview-react/jest.config.ts
Normal file
34
packages/dockview-react/jest.config.ts
Normal 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;
|
59
packages/dockview-react/package.json
Normal file
59
packages/dockview-react/package.json
Normal 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"
|
||||||
|
}
|
||||||
|
}
|
113
packages/dockview-react/rollup.config.js
Normal file
113
packages/dockview-react/rollup.config.js
Normal 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 }),
|
||||||
|
];
|
2
packages/dockview-react/scripts/rollupEntryTarget.ts
Normal file
2
packages/dockview-react/scripts/rollupEntryTarget.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
import '../dist/styles/dockview.css';
|
||||||
|
export * from '../src/index';
|
5
packages/dockview-react/src/__tests__/empty.spec.ts
Normal file
5
packages/dockview-react/src/__tests__/empty.spec.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
describe('empty', () => {
|
||||||
|
test('that passes', () => {
|
||||||
|
expect(true).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
0
packages/dockview-react/src/index.scss
Normal file
0
packages/dockview-react/src/index.scss
Normal file
1
packages/dockview-react/src/index.ts
Normal file
1
packages/dockview-react/src/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from 'dockview';
|
14
packages/dockview-react/tsconfig.esm.json
Normal file
14
packages/dockview-react/tsconfig.esm.json
Normal 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__"]
|
||||||
|
}
|
11
packages/dockview-react/tsconfig.json
Normal file
11
packages/dockview-react/tsconfig.json
Normal 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__"]
|
||||||
|
}
|
5
packages/dockview-react/typedoc.json
Normal file
5
packages/dockview-react/typedoc.json
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"extends": ["../../typedoc.base.json"],
|
||||||
|
"entryPoints": ["src/index.ts"],
|
||||||
|
"exclude": ["**/dist/**"]
|
||||||
|
}
|
56
packages/dockview-vue/README.md
Normal file
56
packages/dockview-vue/README.md
Normal 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>
|
||||||
|
```
|
6
packages/dockview-vue/gulpfile.js
Normal file
6
packages/dockview-vue/gulpfile.js
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
const gulp = require('gulp');
|
||||||
|
const buildfile = require('../../scripts/build');
|
||||||
|
|
||||||
|
buildfile.init();
|
||||||
|
|
||||||
|
gulp.task('run', gulp.series(['sass']));
|
34
packages/dockview-vue/jest.config.ts
Normal file
34
packages/dockview-vue/jest.config.ts
Normal 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;
|
61
packages/dockview-vue/package.json
Normal file
61
packages/dockview-vue/package.json
Normal 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"
|
||||||
|
}
|
||||||
|
}
|
113
packages/dockview-vue/rollup.config.js
Normal file
113
packages/dockview-vue/rollup.config.js
Normal 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 }),
|
||||||
|
];
|
2
packages/dockview-vue/scripts/rollupEntryTarget.ts
Normal file
2
packages/dockview-vue/scripts/rollupEntryTarget.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
import '../dist/styles/dockview.css';
|
||||||
|
export * from '../src/index';
|
5
packages/dockview-vue/src/__tests__/empty.spec.ts
Normal file
5
packages/dockview-vue/src/__tests__/empty.spec.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
describe('empty', () => {
|
||||||
|
test('that passes', () => {
|
||||||
|
expect(true).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
285
packages/dockview-vue/src/dockview/dockview.vue
Normal file
285
packages/dockview-vue/src/dockview/dockview.vue
Normal 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>
|
0
packages/dockview-vue/src/index.scss
Normal file
0
packages/dockview-vue/src/index.scss
Normal file
5
packages/dockview-vue/src/index.ts
Normal file
5
packages/dockview-vue/src/index.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
export * from 'dockview-core';
|
||||||
|
|
||||||
|
import DockviewVue from './dockview/dockview.vue';
|
||||||
|
export { DockviewVue };
|
||||||
|
export * from './dockview/dockview.vue';
|
236
packages/dockview-vue/src/utils.ts
Normal file
236
packages/dockview-vue/src/utils.ts
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
10
packages/dockview-vue/tsconfig.app.json
Normal file
10
packages/dockview-vue/tsconfig.app.json
Normal 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": "."
|
||||||
|
}
|
||||||
|
}
|
10
packages/dockview-vue/tsconfig.build-types.json
Normal file
10
packages/dockview-vue/tsconfig.build-types.json
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
// tsconfig.build-types.json
|
||||||
|
{
|
||||||
|
"include": ["src/**/*"],
|
||||||
|
"exclude": [
|
||||||
|
"node_modules",
|
||||||
|
"**/*.cy.ts",
|
||||||
|
"**/*.spec.ts",
|
||||||
|
"**/__tests__/**/*"
|
||||||
|
]
|
||||||
|
}
|
9
packages/dockview-vue/tsconfig.config.json
Normal file
9
packages/dockview-vue/tsconfig.config.json
Normal 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"]
|
||||||
|
}
|
||||||
|
}
|
5
packages/dockview-vue/tsconfig.json
Normal file
5
packages/dockview-vue/tsconfig.json
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
// tsconfig.json
|
||||||
|
{
|
||||||
|
"files": [],
|
||||||
|
"references": [{ "path": "./tsconfig.config.json" }, { "path": "./tsconfig.app.json" }]
|
||||||
|
}
|
5
packages/dockview-vue/typedoc.json
Normal file
5
packages/dockview-vue/typedoc.json
Normal 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
Loading…
Reference in New Issue
Block a user