feat: vue3 fixes

This commit is contained in:
mathuo 2024-05-09 21:46:01 +01:00
parent 26cd1cc1cc
commit 0ae16cf444
No known key found for this signature in database
GPG Key ID: C6EEDEFD6CA07281
15 changed files with 1448 additions and 1432 deletions

View File

@ -1,6 +1,7 @@
{ {
"packages": [ "packages": [
"packages/dockview-core", "packages/dockview-core",
"packages/dockview-vue",
"packages/dockview" "packages/dockview"
], ],
"sandboxes": [ "sandboxes": [
@ -41,4 +42,4 @@
"/packages/docs/sandboxes/javascript/vanilla-dockview" "/packages/docs/sandboxes/javascript/vanilla-dockview"
], ],
"node": "18" "node": "18"
} }

View File

@ -137,13 +137,6 @@ export class DockviewPanel
if (didTitleChange) { if (didTitleChange) {
this._title = title; this._title = title;
this.view.update({
params: {
params: this._params,
title: this.title,
},
});
this.api._onDidTitleChange.fire({ title }); this.api._onDidTitleChange.fire({ title });
} }
} }
@ -178,10 +171,7 @@ export class DockviewPanel
// update the view with the updated props // update the view with the updated props
this.view.update({ this.view.update({
params: { params: this._params,
params: this._params,
title: this.title,
},
}); });
} }

View File

@ -4,7 +4,7 @@ const config: JestConfigWithTsJest = {
preset: 'ts-jest', preset: 'ts-jest',
roots: ['<rootDir>/packages/dockview-vue'], roots: ['<rootDir>/packages/dockview-vue'],
modulePaths: ['<rootDir>/packages/dockview-vue/src'], modulePaths: ['<rootDir>/packages/dockview-vue/src'],
displayName: { name: 'dockview', color: 'blue' }, displayName: { name: 'dockview-vue', color: 'blue' },
rootDir: '../../', rootDir: '../../',
collectCoverageFrom: [ collectCoverageFrom: [
'<rootDir>/packages/dockview-vue/src/**/*.{js,jsx,ts,tsx}', '<rootDir>/packages/dockview-vue/src/**/*.{js,jsx,ts,tsx}',

View File

@ -18,9 +18,8 @@ import {
getCurrentInstance, getCurrentInstance,
} from 'vue'; } from 'vue';
import { import {
VueContentRenderer,
VueHeaderActionsRenderer, VueHeaderActionsRenderer,
VueTabRenderer, VueRenderer,
VueWatermarkRenderer, VueWatermarkRenderer,
findComponent, findComponent,
} from '../utils'; } from '../utils';
@ -81,7 +80,7 @@ onMounted(() => {
getCurrentInstance()!, getCurrentInstance()!,
options.name options.name
); );
return new VueContentRenderer(component!, getCurrentInstance()!); return new VueRenderer(component!, getCurrentInstance()!);
}, },
createTabComponent(options) { createTabComponent(options) {
let component = findComponent(getCurrentInstance()!, options.name); let component = findComponent(getCurrentInstance()!, options.name);
@ -94,7 +93,7 @@ onMounted(() => {
} }
if (component) { if (component) {
return new VueTabRenderer(component, getCurrentInstance()!); return new VueRenderer(component, getCurrentInstance()!);
} }
return undefined; return undefined;
}, },
@ -175,8 +174,6 @@ onMounted(() => {
*/ */
instance.value = markRaw(dockview); instance.value = markRaw(dockview);
console.log(getCurrentInstance());
emit('ready', { api: new DockviewApi(dockview) }); emit('ready', { api: new DockviewApi(dockview) });
}); });

View File

@ -1,5 +1,7 @@
import type { import type {
DockviewApi,
DockviewGroupPanel, DockviewGroupPanel,
DockviewPanelApi,
GroupPanelPartInitParameters, GroupPanelPartInitParameters,
IContentRenderer, IContentRenderer,
IDockviewPanelHeaderProps, IDockviewPanelHeaderProps,
@ -84,8 +86,8 @@ export function mountVueComponent<T extends Record<string, any>>(
return { return {
update: (newProps: any) => { update: (newProps: any) => {
runningProps = { ...props, newProps }; runningProps = { ...props, ...newProps };
vNode = cloneVNode(vNode, Object.freeze(runningProps)); vNode = cloneVNode(vNode, runningProps);
render(vNode, element); render(vNode, element);
}, },
dispose: () => { dispose: () => {
@ -94,11 +96,13 @@ export function mountVueComponent<T extends Record<string, any>>(
}; };
} }
export class VueContentRenderer implements IContentRenderer { export class VueRenderer implements ITabRenderer, IContentRenderer {
private _element: HTMLElement; private _element: HTMLElement;
private _renderDisposable: private _renderDisposable:
| { update: (props: any) => void; dispose: () => void } | { update: (props: any) => void; dispose: () => void }
| undefined; | undefined;
private _api: DockviewPanelApi | undefined;
private _containerApi: DockviewApi | undefined;
get element(): HTMLElement { get element(): HTMLElement {
return this._element; return this._element;
@ -115,57 +119,9 @@ export class VueContentRenderer implements IContentRenderer {
} }
init(parameters: GroupPanelPartInitParameters): void { init(parameters: GroupPanelPartInitParameters): void {
const props: IDockviewPanelProps = { this._api = parameters.api;
params: parameters.params, this._containerApi = parameters.containerApi;
api: parameters.api,
containerApi: parameters.containerApi,
};
this._renderDisposable?.dispose();
this._renderDisposable = mountVueComponent(
this.component,
this.parent,
{ params: 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,
private readonly parent: ComponentInternalInstance
) {
this._element = document.createElement('div');
this.element.className = 'dv-vue-part';
this.element.style.height = '100%';
this.element.style.width = '100%';
}
init(parameters: GroupPanelPartInitParameters): void {
const props: IDockviewPanelHeaderProps = { const props: IDockviewPanelHeaderProps = {
params: parameters.params, params: parameters.params,
api: parameters.api, api: parameters.api,
@ -182,9 +138,19 @@ export class VueTabRenderer implements ITabRenderer {
} }
update(event: PanelUpdateEvent<Parameters>): void { update(event: PanelUpdateEvent<Parameters>): void {
if (!this._api || !this._containerApi) {
return;
}
const params = event.params; const params = event.params;
// TODO: handle prop updates somehow? // TODO: handle prop updates somehow?
this._renderDisposable?.update(params); this._renderDisposable?.update({
params: {
params: params,
api: this._api,
containerApi: this._containerApi,
},
});
} }
dispose(): void { dispose(): void {
@ -232,9 +198,7 @@ export class VueWatermarkRenderer implements IWatermarkRenderer {
} }
update(event: PanelUpdateEvent<Parameters>): void { update(event: PanelUpdateEvent<Parameters>): void {
const params = event.params; // noop
// TODO: handle prop updates somehow?
this._renderDisposable?.update(params);
} }
dispose(): void { dispose(): void {

View File

@ -3,8 +3,6 @@ import {
DockviewComponent, DockviewComponent,
DockviewWillDropEvent, DockviewWillDropEvent,
DockviewApi, DockviewApi,
IContentRenderer,
ITabRenderer,
DockviewGroupPanel, DockviewGroupPanel,
IHeaderActionsRenderer, IHeaderActionsRenderer,
DockviewDidDropEvent, DockviewDidDropEvent,
@ -16,7 +14,6 @@ import {
PROPERTY_KEYS, PROPERTY_KEYS,
DockviewComponentOptions, DockviewComponentOptions,
DockviewFrameworkOptions, DockviewFrameworkOptions,
IDockviewDisposable,
DockviewDndOverlayEvent, DockviewDndOverlayEvent,
DockviewReadyEvent, DockviewReadyEvent,
} from 'dockview-core'; } from 'dockview-core';

View File

@ -52,7 +52,7 @@ export class ReactPanelContentPart implements IContentRenderer {
} }
public update(event: PanelUpdateEvent) { public update(event: PanelUpdateEvent) {
this.part?.update(event.params); this.part?.update({ params: event.params });
} }
public layout(_width: number, _height: number): void { public layout(_width: number, _height: number): void {

View File

@ -44,7 +44,7 @@ export class ReactPanelHeaderPart implements ITabRenderer {
} }
public update(event: PanelUpdateEvent): void { public update(event: PanelUpdateEvent): void {
this.part?.update(event.params); this.part?.update({ params: event.params });
} }
public layout(_width: number, _height: number): void { public layout(_width: number, _height: number): void {

View File

@ -22,12 +22,21 @@ function onReady(event: DockviewReadyEvent) {
``` ```
</FrameworkSpecific> </FrameworkSpecific>
<FrameworkSpecific framework='Vue'>
<FrameworkSpecific framework='JavaScript'>
```tsx ```tsx
const component = new DockviewComponent({ const App = {
/** options */ name: 'App',
}); methods: {
onReady(event: DockviewReadyEvent) {
const api: DockviewApi = event.api;
},
},
template: `
<dockview-vue
@ready="onReady"
>
</dockview-vue>`,
};
``` ```
</FrameworkSpecific> </FrameworkSpecific>

View File

@ -39,6 +39,37 @@ return <DockviewReact components={components}/>
</FrameworkSpecific> </FrameworkSpecific>
<FrameworkSpecific framework='Vue'>
```tsx
const App = {
name: 'App',
components: {
'component_1': VueComponent1,
'component_2': VueComponent2,
},
methods: {
onReady(event: DockviewReadyEvent) {
event.api.addPanel({
id: 'panel_1',
component: 'component_1'
});
event.api.addPanel({
id: 'panel_2',
component: 'component_2'
});
},
},
template: `
<dockview-vue
@ready="onReady"
>
</dockview-vue>`,
};
```
</FrameworkSpecific>
Each panel has an [api](/docs/api/dockview/panelApi) which is used to control specific Each panel has an [api](/docs/api/dockview/panelApi) which is used to control specific
features on that individual panel. features on that individual panel.
The panel also has access the [group api](/docs/api/dockview/groupApi) and the container The panel also has access the [group api](/docs/api/dockview/groupApi) and the container

View File

@ -31,6 +31,32 @@ return <DockviewReact tabComponents={tabComponents}/>
``` ```
</FrameworkSpecific> </FrameworkSpecific>
<FrameworkSpecific framework='Vue'>
```tsx
const App = {
name: 'App',
components: {
'component_1': VueComponent1,
'tab_1': VueComponent2,
},
methods: {
onReady(event: DockviewReadyEvent) {
event.api.addPanel({
id: 'panel_1',
component: 'component_1',
tabComponent: 'tab_1'
});
},
},
template: `
<dockview-vue
@ready="onReady"
>
</dockview-vue>`,
};
```
</FrameworkSpecific>
<FrameworkSpecific framework='JavaScript'> <FrameworkSpecific framework='JavaScript'>
not implemented not implemented
</FrameworkSpecific> </FrameworkSpecific>
@ -51,6 +77,32 @@ return <DockviewReact defaultTabRenderer={CustomTabRenderer}/>
``` ```
</FrameworkSpecific> </FrameworkSpecific>
<FrameworkSpecific framework='Vue'>
```tsx
const App = {
name: 'App',
components: {
'component_1': VueComponent1,
'tab_1': VueComponent2,
},
methods: {
onReady(event: DockviewReadyEvent) {
event.api.addPanel({
id: 'panel_1',
component: 'component_1',
});
},
},
template: `
<dockview-vue
@ready="onReady"
:defaultTabRenderer='tab_1'
>
</dockview-vue>`,
};
```
</FrameworkSpecific>
<FrameworkSpecific framework='JavaScript'> <FrameworkSpecific framework='JavaScript'>
not implemented not implemented
</FrameworkSpecific> </FrameworkSpecific>

File diff suppressed because it is too large Load Diff

View File

@ -27,7 +27,7 @@ const Panel = defineComponent({
}, },
}, },
mounted() { mounted() {
const disposable = this.api.onDidTitleChange(() => { const disposable = this.params.api.onDidTitleChange(() => {
this.title = this.api.title; this.title = this.api.title;
}); });
this.title = this.api.title; this.title = this.api.title;
@ -64,14 +64,16 @@ const Tab = defineComponent({
name: 'Tab', name: 'Tab',
props: { props: {
params: { params: {
type: Object as PropType<IDockviewPanelHeaderProps>, type: Object as PropType<
IDockviewPanelHeaderProps<{ myValue: number }>
>,
required: true, required: true,
}, },
}, },
data() { data() {
return { return {
title: '', title: '',
value: null, value: this.params.params.myValue,
}; };
}, },
mounted() { mounted() {
@ -80,11 +82,12 @@ const Tab = defineComponent({
}); });
const disposable2 = this.params.api.onDidParametersChange(() => { const disposable2 = this.params.api.onDidParametersChange(() => {
this.value = this.params.myValue; console.log(this.params);
this.value = this.params.params.myValue;
}); });
this.title = this.api.title; this.title = this.api.title;
this.value = this.params.myValue; this.value = this.params.params.myValue;
return () => { return () => {
disposable.dispose(); disposable.dispose();

View File

@ -50,22 +50,35 @@ fs.writeFileSync(
// dockview // dockview
const dockviewPath = path.join(rootDir, 'packages', 'dockview', 'package.json'); const depPackages = ['dockview', 'dockview-vue'];
const dockviewPackageJson = JSON.parse(
fs.readFileSync(dockviewPath).toString()
);
dockviewPackageJson.version = version; for (const depPackage of depPackages) {
dockviewPackageJson.dependencies['dockview-core'] = dockviewPackageJson.version; const dockviewPath = path.join(
rootDir,
'packages',
depPackage,
'package.json'
);
const dockviewPackageJson = JSON.parse(
fs.readFileSync(dockviewPath).toString()
);
fs.writeFileSync(dockviewPath, JSON.stringify(dockviewPackageJson, null, 4)); dockviewPackageJson.version = version;
dockviewPackageJson.dependencies['dockview-core'] =
dockviewPackageJson.version;
// sanity check fs.writeFileSync(
dockviewPath,
JSON.stringify(dockviewPackageJson, null, 4)
);
const dvCore = JSON.parse(fs.readFileSync(dockviewCorePath).toString()); // sanity check
const dv = JSON.parse(fs.readFileSync(dockviewPath).toString());
console.log(`dockview-core version: ${dvCore.version}`); const dvCore = JSON.parse(fs.readFileSync(dockviewCorePath).toString());
console.log( const dv = JSON.parse(fs.readFileSync(dockviewPath).toString());
`dockview version: ${dv.version} dockview-core dependency version: ${dv.dependencies['dockview-core']}`
); console.log(`dockview-core version: ${dvCore.version}`);
console.log(
`${depPackage} version: ${dv.version} dockview-core dependency version: ${dv.dependencies['dockview-core']}`
);
}

View File

@ -6,6 +6,9 @@
}, },
{ {
"path": "./packages/dockview-core" "path": "./packages/dockview-core"
},
{
"path": "./packages/dockview-vue"
} }
] ]
} }