feat: multiple framework support work

This commit is contained in:
mathuo 2024-03-27 21:32:39 +00:00
parent 61f3c252d4
commit b0d98102fc
No known key found for this signature in database
GPG Key ID: C6EEDEFD6CA07281
100 changed files with 3157 additions and 611 deletions

View File

@ -47,6 +47,7 @@
"@typescript-eslint/parser": "^6.17.0",
"@vitejs/plugin-vue": "^5.0.4",
"@vue/tsconfig": "^0.5.1",
"concurrently": "^8.2.2",
"cross-env": "^7.0.3",
"eslint": "^8.56.0",
"fs-extra": "^11.2.0",
@ -71,6 +72,7 @@
"typescript": "^5.3.3",
"vite": "^5.1.5",
"vue": "^3.4.21",
"vue-sfc-loader": "^0.1.0",
"vue-tsc": "^2.0.5"
},
"engines": {

View File

@ -1,12 +1,17 @@
import { Position, positionToDirection } from '../dnd/droptarget';
import { DockviewComponent } from '../dockview/dockviewComponent';
import { DockviewGroupPanel } from '../dockview/dockviewGroupPanel';
import { DockviewGroupLocation } from '../dockview/dockviewGroupPanelModel';
import {
DockviewGroupChangeEvent,
DockviewGroupLocation,
} from '../dockview/dockviewGroupPanelModel';
import { Emitter, Event } from '../events';
import { MutableDisposable } from '../lifecycle';
import { GridviewPanelApi, GridviewPanelApiImpl } from './gridviewPanelApi';
export interface DockviewGroupPanelApi extends GridviewPanelApi {
readonly onDidLocationChange: Event<DockviewGroupPanelFloatingChangeEvent>;
readonly onDidActivePanelChange: Event<DockviewGroupChangeEvent>;
readonly location: DockviewGroupLocation;
/**
* If you require the Window object
@ -27,6 +32,8 @@ export interface DockviewGroupPanelFloatingChangeEvent {
const NOT_INITIALIZED_MESSAGE = 'DockviewGroupPanelApiImpl not initialized';
export class DockviewGroupPanelApiImpl extends GridviewPanelApiImpl {
private readonly _mutableDisposable = new MutableDisposable();
private _group: DockviewGroupPanel | undefined;
readonly _onDidLocationChange =
@ -34,6 +41,10 @@ export class DockviewGroupPanelApiImpl extends GridviewPanelApiImpl {
readonly onDidLocationChange: Event<DockviewGroupPanelFloatingChangeEvent> =
this._onDidLocationChange.event;
private readonly _onDidActivePanelChange =
new Emitter<DockviewGroupChangeEvent>();
readonly onDidActivePanelChange = this._onDidActivePanelChange.event;
get location(): DockviewGroupLocation {
if (!this._group) {
throw new Error(NOT_INITIALIZED_MESSAGE);
@ -44,7 +55,11 @@ export class DockviewGroupPanelApiImpl extends GridviewPanelApiImpl {
constructor(id: string, private readonly accessor: DockviewComponent) {
super(id, '__dockviewgroup__');
this.addDisposables(this._onDidLocationChange);
this.addDisposables(
this._onDidLocationChange,
this._onDidActivePanelChange,
this._mutableDisposable
);
}
close(): void {
@ -116,5 +131,19 @@ export class DockviewGroupPanelApiImpl extends GridviewPanelApiImpl {
initialize(group: DockviewGroupPanel): void {
this._group = group;
/**
* TODO: Annoying initialization order caveat
*
* Due to the order on initialization we know that the model isn't defined until later in the same stack-frame of setup.
* By queuing a microtask we can ensure the setup is completed within the same stack-frame, but after everything else has
* finished ensuring the `model` is defined.
*/
queueMicrotask(() => {
this._mutableDisposable.value =
this._group!.model.onDidActivePanelChange((event) => {
this._onDidActivePanelChange.fire(event);
});
});
}
}

View File

@ -1039,11 +1039,11 @@ export class DockviewComponent
updateOptions(options: Partial<DockviewComponentOptions>): void {
const changed_floatingGroupBounds =
options.floatingGroupBounds !== undefined &&
'floatingGroupBounds' in options &&
options.floatingGroupBounds !== this.options.floatingGroupBounds;
const changed_rootOverlayOptions =
options.rootOverlayModel !== undefined &&
'rootOverlayModel' in options &&
options.rootOverlayModel !== this.options.rootOverlayModel;
this._options = { ...this.options, ...options };

View File

@ -537,6 +537,7 @@ export class DockviewGroupPanelModel
this._rightHeaderActions.init({
containerApi: this._api,
api: this.groupPanel.api,
group: this.groupPanel,
});
this.tabsContainer.setRightActionsElement(
this._rightHeaderActions.element
@ -552,6 +553,7 @@ export class DockviewGroupPanelModel
this._leftHeaderActions.init({
containerApi: this._api,
api: this.groupPanel.api,
group: this.groupPanel,
});
this.tabsContainer.setLeftActionsElement(
this._leftHeaderActions.element
@ -567,6 +569,7 @@ export class DockviewGroupPanelModel
this._prefixHeaderActions.init({
containerApi: this._api,
api: this.groupPanel.api,
group: this.groupPanel,
});
this.tabsContainer.setPrefixActionsElement(
this._prefixHeaderActions.element
@ -845,10 +848,8 @@ export class DockviewGroupPanelModel
this._panels.splice(index, 1);
if (this.mostRecentlyUsed.includes(panel)) {
this.mostRecentlyUsed.splice(
this.mostRecentlyUsed.indexOf(panel),
1
);
const index = this.mostRecentlyUsed.indexOf(panel);
this.mostRecentlyUsed.splice(index, 1);
}
const disposable = this._panelDisposables.get(panel.id);

View File

@ -30,6 +30,7 @@ export interface IDockviewHeaderActionsProps {
export interface IGroupHeaderProps {
api: DockviewGroupPanelApi;
containerApi: DockviewApi;
group: IDockviewGroupPanel;
}
export interface IWatermarkPanelProps {

View File

@ -21,6 +21,9 @@ import {
defineEmits,
watch,
onBeforeUnmount,
markRaw,
toRaw,
getCurrentInstance,
} from 'vue';
import {
VueContentRenderer,
@ -75,11 +78,18 @@ function extractCoreOptions(props: IDockviewVueProps): DockviewOptions {
}
const emit = defineEmits<VueEvents>();
/**
* Anything here that is a Vue.js component should not be reactive
* i.e. markRaw(toRaw(...))
*/
const props = defineProps<IDockviewVueProps>();
const el = ref<HTMLElement | null>(null);
const instance = ref<DockviewComponent | null>(null);
PROPERTY_KEYS.forEach((coreOptionKey) => {
watch(
() => props[coreOptionKey],
@ -141,6 +151,7 @@ watch(
? (group) => {
return new VueHeaderActionsRenderer(
newValue as VueComponent,
getCurrentInstance()!,
group
);
}
@ -159,6 +170,7 @@ watch(
? (group) => {
return new VueHeaderActionsRenderer(
newValue as VueComponent,
getCurrentInstance()!,
group
);
}
@ -176,7 +188,7 @@ watch(
headerPrefixActionComponent: newValue
? (group) => {
return new VueHeaderActionsRenderer(
newValue as VueComponent,
newValue as VueComponent,getCurrentInstance()!,
group
);
}
@ -206,7 +218,7 @@ onMounted(() => {
componentId: string,
component: any
): IContentRenderer => {
return new VueContentRenderer(component);
return new VueContentRenderer(component,getCurrentInstance()!);
},
},
tab: {
@ -215,7 +227,7 @@ onMounted(() => {
componentId: string,
component: any
): ITabRenderer => {
return new VueTabRenderer(component);
return new VueTabRenderer(component,getCurrentInstance()!);
},
},
watermark: {
@ -224,7 +236,7 @@ onMounted(() => {
componentId: string,
component: any
): IWatermarkRenderer => {
return new VueWatermarkRenderer(component);
return new VueWatermarkRenderer(component,getCurrentInstance()!);
},
},
// action: {
@ -238,7 +250,7 @@ onMounted(() => {
headerLeftActionComponent: props.leftHeaderActionsComponent
? (group) => {
return new VueHeaderActionsRenderer(
props.leftHeaderActionsComponent as VueComponent,
props.leftHeaderActionsComponent as VueComponent,getCurrentInstance()!,
group
);
}
@ -246,7 +258,7 @@ onMounted(() => {
headerPrefixActionComponent: props.prefixHeaderActionsComponent
? (group) => {
return new VueHeaderActionsRenderer(
props.prefixHeaderActionsComponent as VueComponent,
props.prefixHeaderActionsComponent as VueComponent,getCurrentInstance()!,
group
);
}
@ -254,7 +266,7 @@ onMounted(() => {
headerRightActionComponent: props.rightHeaderActionsComponent
? (group) => {
return new VueHeaderActionsRenderer(
props.rightHeaderActionsComponent as VueComponent,
props.rightHeaderActionsComponent as VueComponent,getCurrentInstance()!,
group
);
}
@ -270,7 +282,25 @@ onMounted(() => {
...frameworkOptions,
});
instance.value = dockview;
const { clientWidth, clientHeight } = el.value;
dockview.layout(clientWidth, clientHeight);
/**
* !!! THIS IS VERY IMPORTANT
*
* Since we store a reference to `DockviewComponent` within the Vue.js world Vue.js will 'deeply Proxyify' the object
* since this is how Vue.js does its reactivity magic.
*
* We do not want Vue.js to touch the `DockviewComponent` reference since it does not need to be reactive in accordance
* to the Vue.js reactivity model and since `DockviewComponent` is written in plain TypeScript allowing Vue.js
* to proxify the reference will cause all kinds of unexpected issues
*
* @see https://vuejs.org/guide/extras/reactivity-in-depth.html
* @see https://vuejs.org/api/reactivity-advanced.html#markraw
*/
instance.value = markRaw(dockview);
emit('ready', { api: new DockviewApi(dockview) });
});

View File

@ -1,9 +1,8 @@
import type {
DockviewApi,
DockviewGroupPanel,
DockviewGroupPanelApi,
GroupPanelPartInitParameters,
IContentRenderer,
IGroupHeaderProps,
IHeaderActionsRenderer,
ITabRenderer,
IWatermarkRenderer,
@ -18,6 +17,7 @@ import {
cloneVNode,
mergeProps,
type DefineComponent,
type ComponentInternalInstance,
} from 'vue';
export type ComponentInterface = ComponentOptionsBase<
@ -45,16 +45,22 @@ export type VueComponent<T = any> = DefineComponent<T>;
*/
export function mountVueComponent<T extends Record<string, any>>(
component: VueComponent<T>,
parent: ComponentInternalInstance,
props: T,
element: HTMLElement
) {
let vNode = createVNode(component, props);
let vNode = createVNode(component, Object.freeze(props));
vNode.appContext = parent.appContext;
render(vNode, element);
let runningProps = props;
return {
update: (newProps: any) => {
vNode = cloneVNode(vNode, mergeProps(props, newProps));
runningProps = { ...props, newProps };
vNode = cloneVNode(vNode, Object.freeze(runningProps));
render(vNode, element);
},
dispose: () => {
@ -73,7 +79,10 @@ export class VueContentRenderer implements IContentRenderer {
return this._element;
}
constructor(private readonly component: VueComponent) {
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%';
@ -90,6 +99,7 @@ export class VueContentRenderer implements IContentRenderer {
this._renderDisposable?.dispose();
this._renderDisposable = mountVueComponent(
this.component,
this.parent,
props,
this.element
);
@ -120,7 +130,10 @@ export class VueTabRenderer implements ITabRenderer {
return this._element;
}
constructor(private readonly component: VueComponent) {
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%';
@ -137,6 +150,7 @@ export class VueTabRenderer implements ITabRenderer {
this._renderDisposable?.dispose();
this._renderDisposable = mountVueComponent(
this.component,
this.parent,
props,
this.element
);
@ -163,7 +177,10 @@ export class VueWatermarkRenderer implements IWatermarkRenderer {
return this._element;
}
constructor(private readonly component: VueComponent) {
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%';
@ -179,6 +196,7 @@ export class VueWatermarkRenderer implements IWatermarkRenderer {
this._renderDisposable?.dispose();
this._renderDisposable = mountVueComponent(
this.component,
this.parent,
props,
this.element
);
@ -211,32 +229,27 @@ export class VueHeaderActionsRenderer implements IHeaderActionsRenderer {
constructor(
private readonly component: VueComponent,
private readonly parent: ComponentInternalInstance,
group: DockviewGroupPanel
) {
this._element = document.createElement('div');
this.element.className = 'dv-vue-header-action-part';
this._element.style.width = '100%';
this._element.style.height = '100%';
}
init(params: {
containerApi: DockviewApi;
api: DockviewGroupPanelApi;
}): void {
console.log('meeee', this.component);
const props = {
api: params.api,
containerApi: params.containerApi,
};
init(params: IGroupHeaderProps): void {
console.log(params);
this._renderDisposable?.dispose();
this._renderDisposable = mountVueComponent(
this.component,
props,
this.parent,
{ ...params },
this.element
);
}
dispose(): void {
console.log('dispose');
this._renderDisposable?.dispose();
}
}

View File

@ -129,24 +129,29 @@ export const DockviewReact = React.forwardRef(
const prevProps = React.useRef<Partial<IDockviewReactProps>>({});
React.useEffect(() => {
const changes: Partial<DockviewOptions> = {};
React.useEffect(
() => {
const changes: Partial<DockviewOptions> = {};
Object.keys(PROPERTY_KEYS).forEach((propKey) => {
const key = propKey as keyof DockviewOptions;
const propValue = props[key];
PROPERTY_KEYS.forEach((propKey) => {
const key = propKey as keyof DockviewOptions;
const propValue = props[key];
if (propValue !== prevProps.current[key]) {
changes[key] = propValue as any;
if (propValue !== prevProps.current[key]) {
changes[key] = propValue as any;
}
});
if (dockviewRef.current) {
dockviewRef.current.updateOptions(changes);
} else {
// not yet fully initialized
}
});
if (dockviewRef.current) {
dockviewRef.current.updateOptions(changes);
} else {
// not yet fully initialized
}
}, PROPERTY_KEYS.map((key) => props[key]).filter(Boolean));
prevProps.current = props;
},
PROPERTY_KEYS.map((key) => props[key])
);
React.useEffect(() => {
if (!domRef.current) {

View File

@ -1,37 +0,0 @@
---
slug: dockview-1.12.0-release
title: Dockview 1.12.0
tags: [release]
---
# Release Notes
Please reference docs @ [dockview.dev](https://dockview.dev).
## 🛠 Miscs
- Create framework packages in preperation for multiple framework support
These are still in active development and will be offical support soon.
- Create `dockview-react` package
- Create `dockview-angular` package
- Create `dockview-vue` package
- Move various type definitions from `dockview` to `dockview-core` in preperation for multiple framework support
- Move `IGroupPanelBaseProps` from `dockview` to `dockview-core`
- Move `IDockviewPanelHeaderProps` from `dockview` to `dockview-core`
- Move `IDockviewPanelProps` from `dockview` to `dockview-core`
- Move `IDockviewHeaderActionsProps ` from `dockview` to `dockview-core`
- Move `IGroupHeaderProps` from `dockview` to `dockview-core`
- Move `IWatermarkPanelProps` from `dockview` to `dockview-core`
- Move `DockviewReadyEvent` from `dockview` to `dockview-core`
- Depreciate `dockview` (React) `canDisplayOverlay` in favour of the `onUnhandledDragOverEvent` api event
## 🔥 Breaking changes
- Replace `dockview-core` DockviewComponent `canDisplayOverlay` option with `onUnhandledDragOverEvent` event
- Rename `createRightHeaderActionsElement` to `headerRightActionComponent`
- Rename `createLeftHeaderActionsElement` to `headerLeftActionComponent`
- Rename `createPrefixHeaderActionsElement` to `headerPrefixActionComponent`

View File

@ -0,0 +1,41 @@
---
slug: dockview-1.13.0-release
title: Dockview 1.13.0
tags: [release]
---
# Release Notes
Please reference docs @ [dockview.dev](https://dockview.dev).
## 🚀 Features
- Add `onDidActivePanelChange` event to group api [#541](https://github.com/mathuo/dockview/pull/541)
## 🛠 Miscs
- Create framework packages in preperation for multiple framework support [#541](https://github.com/mathuo/dockview/pull/541)
These are still in active development and will be offically support soon.
- Create `dockview-react` package
- Create `dockview-angular` package
- Create `dockview-vue` package
- Move various type definitions from `dockview` to `dockview-core` in preperation for multiple framework support [#541](https://github.com/mathuo/dockview/pull/541)
- Move `IGroupPanelBaseProps` from `dockview` to `dockview-core`
- Move `IDockviewPanelHeaderProps` from `dockview` to `dockview-core`
- Move `IDockviewPanelProps` from `dockview` to `dockview-core`
- Move `IDockviewHeaderActionsProps ` from `dockview` to `dockview-core`
- Move `IGroupHeaderProps` from `dockview` to `dockview-core`
- Move `IWatermarkPanelProps` from `dockview` to `dockview-core`
- Move `DockviewReadyEvent` from `dockview` to `dockview-core`
- [dockview] Depreciate `canDisplayOverlay` in favour of the `onUnhandledDragOverEvent` api event [#541](https://github.com/mathuo/dockview/pull/541)
## 🔥 Breaking changes
- [dockview-core] Replace DockviewComponent `canDisplayOverlay` option with `onUnhandledDragOverEvent` event [#541](https://github.com/mathuo/dockview/pull/541)
- [dockview-core] Rename `createRightHeaderActionsElement` to `headerRightActionComponent` [#541](https://github.com/mathuo/dockview/pull/541)
- [dockview-core] Rename `createLeftHeaderActionsElement` to `headerLeftActionComponent` [#541](https://github.com/mathuo/dockview/pull/541)
- [dockview-core] Rename `createPrefixHeaderActionsElement` to `headerPrefixActionComponent` [#541](https://github.com/mathuo/dockview/pull/541)

View File

@ -3,12 +3,11 @@ title: Nested Instances
---
import { MultiFrameworkContainer } from '@site/src/components/ui/container';
import NestedDockview from '@site/sandboxes/nested-dockview/src/app';
import { CodeRunner } from '@site/src/components/ui/codeRunner';
# Nested Dockviews
You can safely create multiple dockview instances within one page and nest dockviews within other dockviews.
If you wish to interact with the drop event from one dockview instance in another dockview instance you can implement the `showDndOverlay` and `onDidDrop` props on `DockviewReact`.
<MultiFrameworkContainer sandboxId="nested-dockview" react={NestedDockview} />
<CodeRunner id="dockview/nested" />

View File

@ -2,7 +2,6 @@
title: Constraints
---
import LiveExample from '@site/src/components/ui/exampleFrame';
import { DocRef } from '@site/src/components/ui/reference/docRef'
:::warning
@ -13,5 +12,5 @@ Constraints come with several caveats. They are not serialized with layouts and
## Live Example
<LiveExample framework="react" id="dockview/constraints"/>
<CodeRunner id="dockview/constraints"/>

View File

@ -2,7 +2,6 @@
title: Group Controls
---
import LiveExample from '@site/src/components/ui/exampleFrame';
import { DocRef } from '@site/src/components/ui/reference/docRef';
This section describes how you can customize the header component of each group.
@ -39,5 +38,5 @@ return <DockviewReact
## Live Example
<LiveExample framework="react" id="dockview/group-actions"/>
<CodeRunner id="dockview/group-actions"/>

View File

@ -3,7 +3,6 @@ title: Floating Groups
---
import useBaseUrl from '@docusaurus/useBaseUrl';
import LiveExample from '@site/src/components/ui/exampleFrame';
import { DocRef } from '@site/src/components/ui/reference/docRef';
This section describes floating groups.
@ -76,4 +75,4 @@ You can check whether a group is floating via the `group.api.location` property.
## Live Example
<LiveExample framework="react" id="dockview/floating-groups"/>
<CodeRunner id="dockview/floating-groups"/>

View File

@ -3,7 +3,6 @@ title: Hidden Header
---
import useBaseUrl from '@docusaurus/useBaseUrl';
import LiveExample from '@site/src/components/ui/exampleFrame';
import { DocRef } from '@site/src/components/ui/reference/docRef';
You may wish to hide the header section of a group. This can achieved through the `hidden` variable on `panel.group.header`.

View File

@ -2,8 +2,7 @@
title: Locked Groups
---
import { MultiFrameworkContainer } from '@site/src/components/ui/container';
import DockviewLockedGroup from '@site/sandboxes/lockedgroup-dockview/src/app';
import { CodeRunner } from '@site/src/components/ui/codeRunner';
## Locked group
@ -21,7 +20,4 @@ panel.group.locked = 'no-drop-target';
Use `true` to keep drop zones top, right, bottom, left for the group. Use `no-drop-target` to disable all drop zones. For you to get a
better understanding of what this means, try and drag the panels in the example below to the locked groups.
<MultiFrameworkContainer
sandboxId="lockedgroup-dockview"
react={DockviewLockedGroup}
/>
<CodeRunner id="dockview/locked"/>

View File

@ -3,7 +3,6 @@ title: Maximized Groups
---
import { DocRef } from '@site/src/components/ui/reference/docRef';
import LiveExample from '@site/src/components/ui/exampleFrame';
This section described how to maxmimize groups.
@ -51,7 +50,7 @@ The methods exist on the panel `api` object for convenience.
## Live Examples
<LiveExample framework="react" id="dockview/maximize-group"/>
<CodeRunner id="dockview/maximize-group"/>

View File

@ -2,7 +2,6 @@
title: Popout Windows
---
import LiveExample from '@site/src/components/ui/exampleFrame';
import { DocRef } from '@site/src/components/ui/reference/docRef';
This section describes have to create popout windows.
@ -57,4 +56,4 @@ in it's original location within the grid. If the dock cannot determine the orig
choose a new location.
<LiveExample framework="react" id="dockview/popout-group"/>
<CodeRunner id="dockview/popout-group"/>

View File

@ -2,7 +2,6 @@
title: Resizing
---
import LiveExample from '@site/src/components/ui/exampleFrame';
import { DocRef } from '@site/src/components/ui/reference/docRef';
<DocRef declaration="DockviewGroupPanelApi" methods={['height', 'width', 'setSize', 'onDidDimensionsChange']} />
@ -34,4 +33,4 @@ props.api.group.api.setSize({
You can see an example invoking both approaches below.
<LiveExample framework="react" id="dockview/resize"/>
<CodeRunner framework="react" id="dockview/resize"/>

View File

@ -5,7 +5,6 @@ title: Locked
import useBaseUrl from '@docusaurus/useBaseUrl';
import { DocRef } from '@site/src/components/ui/reference/docRef';
import LiveExample from '@site/src/components/ui/exampleFrame';
This section describes how to lock the dock to prevent movement.
@ -13,4 +12,4 @@ This section describes how to lock the dock to prevent movement.
You may want to combine this with `disableDnd={true}` to provide a locked grid with no dnd funtionality. See [Disable Dnd](/docs/core/dnd/disable) for more.
:::
<LiveExample framework='react' id='dockview/locked'/>
<CodeRunner id='dockview/locked'/>

View File

@ -3,8 +3,6 @@ title: Overview
sidebar_position: 0
---
import LiveExample from '@site/src/components/ui/exampleFrame';
This section provided a core overview.
The component takes a collection of [Options](/docs/api/dockview/options) as inputs and
@ -33,10 +31,3 @@ const component = new DockviewComponent({
```
</FrameworkSpecific>
## Container Resizing
The component will automatically resize to it's container.
<LiveExample framework="react" id="dockview/resize-container"/>
# Disposal Pattern

View File

@ -4,7 +4,7 @@ sidebar_position: 1
---
import { DocRef } from '@site/src/components/ui/reference/docRef';
import LiveExample from '@site/src/components/ui/exampleFrame';
import { CodeRunner } from '@site/src/components/ui/codeRunner';
This section describes how to add a new panel and the options you can provide.
@ -48,7 +48,7 @@ api.addPanel({
api.setTitle('my_new_custom_title');
```
<LiveExample framework="react" id="dockview/update-title" height={250}/>
<CodeRunner id="dockview/update-title" height={250}/>
## Provide a custom Tab renderer

View File

@ -4,11 +4,9 @@ sidebar_postiion: 5
---
import { MultiFrameworkContainer } from '@site/src/components/ui/container';
import { CodeRunner } from '@site/src/components/ui/codeRunner';
import RenderingDockview from '@site/sandboxes/rendering-dockview/src/app';
import LiveExample from '@site/src/components/ui/exampleFrame';
Rendering type is an important consideration when creating your application and whether your panels should be destroyed when hidden.
:::info
@ -70,7 +68,7 @@ api.addPanel({
## Live Example
<LiveExample framework="react" id="dockview/render-mode"/>
<CodeRunner id="dockview/render-mode"/>
By default `DockviewReact` only adds to the DOM those panels that are visible,
@ -130,3 +128,4 @@ Toggling the checkbox you can see that when you only render those panels which a
sandboxId="rendering-dockview"
react={RenderingDockview}
/>

View File

@ -4,7 +4,7 @@ title: Resizing
This section describes how to programatically resize a panel.
import LiveExample from '@site/src/components/ui/exampleFrame';
import { CodeRunner } from '@site/src/components/ui/codeRunner';
import { DocRef } from '@site/src/components/ui/reference/docRef';
<DocRef declaration="DockviewPanelApi" methods={['height', 'width', 'setSize', 'onDidDimensionsChange']} />
@ -37,4 +37,4 @@ props.api.group.api.setSize({
You can see an example invoking both approaches below.
<LiveExample framework="react" id="dockview/resize"/>
<CodeRunner id="dockview/resize"/>

View File

@ -4,7 +4,7 @@ sidebar_position: 2
---
import { MultiFrameworkContainer } from '@site/src/components/ui/container';
import CustomHeadersDockview from '@site/sandboxes/customheader-dockview/src/app';
import { CodeRunner } from '@site/src/components/ui/codeRunner';
import DockviewNative from '@site/sandboxes/fullwidthtab-dockview/src/app';
import { attach as attachNativeDockview } from '@site/sandboxes/javascript/fullwidthtab-dockview/src/app';
import { DocRef } from '@site/src/components/ui/reference/docRef';
@ -99,10 +99,7 @@ As a simple example the below attaches a custom event handler for the context me
The below example uses a custom tab renderer to reigster a popover when the user right clicked on a tab.
This still makes use of the `DockviewDefaultTab` since it's only a minor change.
<MultiFrameworkContainer
sandboxId="customheader-dockview"
react={CustomHeadersDockview}
/>
<CodeRunner id="dockview/custom-header"/>
## Full Width Tab
@ -126,15 +123,7 @@ return <DockviewReactComponent singleTabMode="fullwidth" />
typescript={attachNativeDockview}
/>
import DockviewTabheight from '@site/sandboxes/tabheight-dockview/src/app';
import { attach as attachTabHeightDockview } from '@site/sandboxes/javascript/tabheight-dockview/src/app';
## Tab Height
Tab height can be controlled through CSS.
<MultiFrameworkContainer
sandboxId="tabheight-dockview"
react={DockviewTabheight}
typescript={attachTabHeightDockview}
/>

View File

@ -43,4 +43,4 @@ panel.api.updateParameters({
## Live Example
<LiveExample framework="react" id="dockview/update-parameters"/>
<CodeRunner id="dockview/update-parameters"/>

View File

@ -2,8 +2,6 @@
title: Scrolling
---
import LiveExample from '@site/src/components/ui/exampleFrame';
It's important to understand how to configure the scrollbar within a panel.
A panel will appear with a scrollbar if the the contents of your view has a fixed height.
@ -17,4 +15,4 @@ The following example contains three views:
- **Panel 2** (`height: 2000px`): A scrollbar does appear since a fixed height has been used.
- **Panel 3**: `height: 100%` and a child component with `overflow: auto` which will enable scrollbars.
<LiveExample framework="react" id="dockview/scrollbars"/>
<CodeRunner id="dockview/scrollbars"/>

View File

@ -2,8 +2,7 @@
title: Loading State
---
import { MultiFrameworkContainer } from '@site/src/components/ui/container';
import LiveExample from '@site/src/components/ui/exampleFrame';
import { CodeRunner } from '@site/src/components/ui/codeRunner';
import { DocRef } from '@site/src/components/ui/reference/docRef';
This section described loading a dock layout.
@ -43,5 +42,6 @@ return <DockviewComponent onReady={onReady}/>;
# Live Example
<LiveExample framework="react" id="dockview/layout"/>
<CodeRunner id="dockview/layout"/>

View File

@ -2,9 +2,7 @@
title: Saving State
---
import { MultiFrameworkContainer } from '@site/src/components/ui/container';
import LiveExample from '@site/src/components/ui/exampleFrame';
import { CodeRunner } from '@site/src/components/ui/codeRunner';
import { DocRef } from '@site/src/components/ui/reference/docRef';
This section describes how to serialize a dockview instance.
@ -39,5 +37,4 @@ return <DockviewComponent onReady={onReady}/>
# Live Example
<LiveExample framework="react" id="dockview/layout"/>
<CodeRunner id="dockview/layout"/>

View File

@ -5,7 +5,6 @@ title: Watermark
import useBaseUrl from '@docusaurus/useBaseUrl';
import { DocRef } from '@site/src/components/ui/reference/docRef';
import LiveExample from '@site/src/components/ui/exampleFrame';
When there is nothing else to display.
@ -28,4 +27,4 @@ The following properties can be set to configure the behaviours of floating grou
## Live Examples
<LiveExample framework="react" id="dockview/watermark"/>
<CodeRunner id="dockview/watermark"/>

View File

@ -5,6 +5,4 @@ sidebar_position: 3
A *tabview* can be created using a dock and preventing some default behaviours.
import LiveExample from '@site/src/components/ui/exampleFrame';
<LiveExample framework='react' id='dockview/tabview' />
<CodeRunner id='dockview/tabview' />

View File

@ -7,7 +7,7 @@ sidebar_position: 0
Learn how to install Dockview for a selection of frameworks.
<FrameworkSpecific framework='JavaScript'>
Firstly, install the `dockvire-core` library:
Firstly, install the `dockview-core` library:
```sh
npm install dockview-core
@ -22,8 +22,3 @@ npm install dockview
```
</FrameworkSpecific>
import { IsolatedCodeExample } from '@site/src/components/ui/isolatedCodeExample';
<IsolatedCodeExample id="dockview/basic" framework='react'/>
<IsolatedCodeExample id="dockview/basic" framework='vue'/>

View File

@ -5,9 +5,7 @@ description: Gridview Documentation
import { MultiFrameworkContainer } from '@site/src/components/ui/container';
import SimpleGridview from '@site/sandboxes/simple-gridview/src/app';
import EditorGridview from '@site/sandboxes/editor-gridview/src/app';
// import SimpleGridview from '@site/sandboxes/simple-gridview/src/app';
import { EventsGridview } from '@site/src/components/gridview/events';
// import IDEExample from '@site/sandboxes/ide-example/src/app';
import Link from '@docusaurus/Link';
import { DocRef } from '@site/src/components/ui/reference/docRef';

View File

@ -4,10 +4,9 @@ sidebar_position: 0
---
import { MultiFrameworkContainer } from '@site/src/components/ui/container';
import { CodeRunner } from '@site/src/components/ui/codeRunner';
import SimpleDockview from '@site/sandboxes/simple-dockview/src/app';
import DockviewExampleApp from '@site/sandboxes/example-app-dockview/src/app';
import { attach as attachSimpleDockview } from '@site/sandboxes/javascript/simple-dockview/src/app';
<MultiFrameworkContainer
@ -20,11 +19,7 @@ import { attach as attachSimpleDockview } from '@site/sandboxes/javascript/simpl
Dockview is an abstraction built on top of [Gridviews](./gridview) where each view is a container of many tabbed panels.
<MultiFrameworkContainer
sandboxId="simple-dockview"
react={SimpleDockview}
typescript={attachSimpleDockview}
/>
<CodeRunner id="dockview/basic"/>
<br />
@ -60,19 +55,3 @@ const MyComponent = (props: IDockviewPanelProps<{ title: string }>) => {
return <div>{`My first panel has the title: ${props.params.title}`}</div>;
};
```

View File

@ -4,12 +4,9 @@ description: A zero dependency layout manager supporting ReactJS and Vanilla Typ
title: Introduction
---
import { MultiFrameworkContainer } from '@site/src/components/ui/container';
import { SimpleSplitview } from '@site/src/components/simpleSplitview';
import { SimpleGridview } from '@site/src/components/simpleGridview';
import { SimplePaneview } from '@site/src/components/simplePaneview';
// import DockviewDemo from '@site/sandboxes/demo-dockview/src/app';
import Link from '@docusaurus/Link';
**dockview** is a zero dependency layout manager that supports tab, grids and splitviews.
@ -35,11 +32,6 @@ There are 4 components you may want to use:
<h2>Dockview</h2>
</Link>
{/* <MultiFrameworkContainer
height={500}
sandboxId="demo-dockview"
react={DockviewDemo}
/> */}
<Link to="./components/splitview">
<h2>Splitview</h2>

View File

@ -5,11 +5,13 @@
"scripts": {
"build": "npm run create-templates && docusaurus build",
"clear": "docusaurus clear",
"start": "docusaurus start",
"start": "concurrently \"docusaurus start\" \"npm run start-esm-server\"",
"start-esm-server": "node web-server/index.mjs",
"swizzle": "docusaurus swizzle @docusaurus/theme-classic",
"docs:version": "docusaurus docs:version",
"typecheck": "tsc",
"create-templates": "node scripts/buildTemplates.mjs"
"create-templates": "node scripts/buildTemplates.mjs",
"create-templates:local": "node scripts/buildTemplates.mjs --local"
},
"browserslist": {
"production": [

View File

@ -1,5 +1,6 @@
import fs from 'fs-extra';
import * as path from 'path';
import { argv } from 'process';
import { fileURLToPath } from 'url';
@ -8,7 +9,7 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url));
const REACT_VERSION = '18.2.0';
const VUE_VERSION = '3.4.21';
const DOCKVIEW_VERSION = '1.11.0';
const USE_LOCAL_CDN = true;
const USE_LOCAL_CDN = argv.slice(2).includes('--local');
const local = 'http://localhost:1111';
@ -35,7 +36,7 @@ const DOCKVIEW_CDN = {
'dockview-vue': `${local}/dockview-vue/dist/dockview-vue.es.js`,
},
},
js: {
typescript: {
remote: {
'dockview-core': `https://cdn.jsdelivr.net/npm/dockview-core@${DOCKVIEW_VERSION}/dist/dockview-core.esm.js`,
'dockview-core/': `https://cdn.jsdelivr.net/npm/dockview-core@${DOCKVIEW_VERSION}/`,
@ -57,8 +58,9 @@ const IMPORTS_PATHS = {
vue: {
vue: `https://cdn.jsdelivr.net/npm/vue@${VUE_VERSION}/dist/vue.esm-browser.js`,
'@vue/reactivity': `https://esm.sh/@vue/reactivity@${VUE_VERSION}`,
'vue-sfc-loader': `https://cdn.jsdelivr.net/npm/vue3-sfc-loader@0.9.5/dist/vue3-sfc-loader.js`,
},
js: {},
typescript: {},
angular: {},
};
@ -82,7 +84,7 @@ const input_dir = path.join(__dirname, '../templates');
const output = path.join(__dirname, '../static/templates');
const COMPONENTS = ['dockview'];
const FRAMEWORKS = ['react', 'vue'];
const FRAMEWORKS = ['react', 'vue', 'typescript'];
const list = [];

View File

@ -3,7 +3,6 @@ import HomepageFeatures from '.';
import { BrowserHeader } from '../ui/browserHeader';
import { MultiFrameworkContainer } from '../ui/container';
import * as React from 'react';
// import DockviewDemo from '@site/sandboxes/demo-dockview/src/app';
import DockviewDemo2 from '@site/sandboxes/dockview-app/src/app';
export const Introduction = () => {
@ -25,11 +24,6 @@ export const Introduction = () => {
</div>
<div style={{ padding: '20px' }}>
<BrowserHeader />
{/* <MultiFrameworkContainer
height={750}
sandboxId="demo-dockview"
react={DockviewDemo}
/> */}
</div>
<div style={{ padding: '20px' }}>
<BrowserHeader />

View File

@ -11,7 +11,7 @@ const frameworks = [
const activeFrameworkGlobal = new DockviewEmitter<string>({ replay: true });
function useActiveFramework(): [string, (value: string) => void] {
export function useActiveFramework(): [string, (value: string) => void] {
const [value, setValue] = React.useState<string>(
localStorage.getItem('dv-docs-framework') ?? frameworks[0].value
);

View File

@ -0,0 +1,66 @@
import * as React from 'react';
import { useActiveFramework } from '../frameworkSpecific';
import BrowserOnly from '@docusaurus/BrowserOnly';
const BASE_SANDBOX_URL =
'https://codesandbox.io/s/github/mathuo/dockview/tree/master/packages/docs';
export const _CodeRunner = (props: {
id: string;
framework: string;
height: number;
}) => {
useActiveFramework();
const sandboxUrl = `${BASE_SANDBOX_URL}/templates/${props.id}/${props.framework}`;
const path = `/templates/${props.id}/${props.framework}/index.html`;
return (
<div>
<iframe
src={path}
style={{ width: '100%', height: `${props.height ?? 300}px` }}
/>
<div style={{ display: 'flex', justifyContent: 'flex-end' }}>
<CodeSandbox url={sandboxUrl} />
</div>
</div>
);
};
export const CodeRunner = (props: { id: string; height: number }) => {
const [framework] = useActiveFramework();
return (
<BrowserOnly>
{() => (
<_CodeRunner {...props} framework={framework.toLowerCase()} />
)}
</BrowserOnly>
);
};
const CodeSandbox = (props: { url: string }) => {
return (
<span
className="codesandbox-button"
style={{ display: 'flex', alignItems: 'center' }}
>
<span className="codesandbox-button-pretext">{`Open in `}</span>
<a
href={props.url}
target={'_blank'}
rel="noopener"
className="codesandbox-button-content"
>
<span
style={{
fontWeight: 'bold',
paddingRight: '4px',
}}
>
CodeSandbox
</span>
</a>
</span>
);
};

View File

@ -1,16 +0,0 @@
import * as React from 'react';
export const IsolatedCodeExample = (props: {
id: string;
framework: string;
height: number;
}) => {
const path = `/templates/${props.id}/${props.framework}/index.html`;
return (
<iframe
src={path}
style={{ width: '100%', height: `${props.height ?? 300}px` }}
/>
);
};

View File

@ -2,6 +2,7 @@ import React from 'react';
// Import the original mapper
import MDXComponents from '@theme-original/MDXComponents';
import { FrameworkSpecific } from '@site/src/components/frameworkSpecific';
import { CodeRunner } from '../components/ui/codeRunner';
export default {
// Re-use the default mapping
@ -9,4 +10,5 @@ export default {
// Map the "<Highlight>" tag to our Highlight component
// `Highlight` will receive all props that were passed to `<Highlight>` in MDX
FrameworkSpecific,
CodeRunner,
};

View File

@ -0,0 +1,47 @@
import 'dockview-core/dist/styles/dockview.css';
import {
DockviewApi,
DockviewComponent,
GroupPanelPartInitParameters,
IContentRenderer,
} from 'dockview-core';
class Panel implements IContentRenderer {
private readonly _element: HTMLElement;
get element(): HTMLElement {
return this._element;
}
constructor() {
this._element = document.createElement('div');
this._element.style.color = 'white';
}
init(parameters: GroupPanelPartInitParameters): void {
this._element.textContent = 'Hello World';
}
}
document.getElementById('app').className = 'dockview-theme-abyss';
const dockview = new DockviewComponent({
parentElement: document.getElementById('app'),
components: { default: Panel },
});
const api = new DockviewApi(dockview);
api.addPanel({
id: 'panel_1',
component: 'default',
title: 'Panel 1',
});
api.addPanel({
id: 'panel_2',
component: 'default',
position: { referencePanel: 'panel_1', direction: 'right' },
title: 'Panel 2',
});

View File

@ -36,7 +36,7 @@ const App = defineComponent({
'dockview-vue': DockviewVue,
Panel,
},
setup() {
data() {
return {
components: {
panel: Panel,
@ -45,8 +45,6 @@ const App = defineComponent({
},
methods: {
onReady(event: DockviewReadyEvent) {
console.log('ready');
event.api.addPanel({
id: 'panel_1',
component: 'panel',

View File

@ -9,9 +9,5 @@ const rootElement = document.getElementById('app');
if (rootElement) {
const root = ReactDOMClient.createRoot(rootElement);
root.render(
<div className="app">
<App />
</div>
);
root.render(<App />);
}

View File

@ -0,0 +1,32 @@
{
"name": "dockview.update-parameters",
"description": "",
"keywords": [
"dockview"
],
"version": "1.0.0",
"main": "src/index.tsx",
"dependencies": {
"dockview": "*",
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
"devDependencies": {
"@types/react": "^18.0.28",
"@types/react-dom": "^18.0.11",
"typescript": "^4.9.5",
"react-scripts": "*"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject"
},
"browserslist": [
">0.2%",
"not dead",
"not ie <= 11",
"not op_mini all"
]
}

View File

@ -0,0 +1,85 @@
import {
DockviewReact,
DockviewReadyEvent,
IDockviewPanelHeaderProps,
IDockviewPanelProps,
} from 'dockview';
import React from 'react';
interface CustomParams {
myValue: number;
}
const components = {
default: (props: IDockviewPanelProps<CustomParams>) => {
const [running, setRunning] = React.useState<boolean>(false);
React.useEffect(() => {
if (!running) {
return;
}
const interval = setInterval(() => {
props.api.updateParameters({ myValue: Date.now() });
}, 1000);
props.api.updateParameters({ myValue: Date.now() });
return () => {
clearInterval(interval);
};
}, [running]);
return (
<div style={{ height: '100%', padding: '20px', color: 'white' }}>
<div>{props.api.title}</div>
<button onClick={() => setRunning(!running)}>
{running ? 'Stop' : 'Start'}
</button>
<span>{`value: ${props.params.myValue}`}</span>
</div>
);
},
};
const tabComponents = {
default: (props: IDockviewPanelHeaderProps<CustomParams>) => {
return (
<div>
<div>{`custom tab: ${props.api.title}`}</div>
<span>{`value: ${props.params.myValue}`}</span>
</div>
);
},
};
export const App: React.FC = (props: { theme?: string }) => {
const onReady = (event: DockviewReadyEvent) => {
event.api.addPanel({
id: 'panel_1',
component: 'default',
tabComponent: 'default',
params: {
myValue: Date.now(),
},
});
event.api.addPanel({
id: 'panel_2',
component: 'default',
tabComponent: 'default',
params: {
myValue: Date.now(),
},
});
};
return (
<DockviewReact
components={components}
tabComponents={tabComponents}
onReady={onReady}
className={props.theme || 'dockview-theme-abyss'}
/>
);
};
export default App;

View File

@ -0,0 +1,13 @@
import React from 'react';
import ReactDOMClient from 'react-dom/client';
import 'dockview/dist/styles/dockview.css';
import App from './app.tsx';
const rootElement = document.getElementById('app');
if (rootElement) {
const root = ReactDOMClient.createRoot(rootElement);
root.render(<App />);
}

View File

@ -0,0 +1,18 @@
{
"compilerOptions": {
"outDir": "build/dist",
"module": "esnext",
"target": "es5",
"lib": ["es6", "dom"],
"sourceMap": true,
"allowJs": true,
"jsx": "react-jsx",
"moduleResolution": "node",
"rootDir": "src",
"forceConsistentCasingInFileNames": true,
"noImplicitReturns": true,
"noImplicitThis": true,
"noImplicitAny": true,
"strictNullChecks": true
}
}

View File

@ -0,0 +1,190 @@
import 'dockview-core/dist/styles/dockview.css';
import { PropType, createApp, defineComponent } from 'vue';
import { DockviewVue } from 'dockview-vue';
import {
DockviewReadyEvent,
IDockviewPanelHeaderProps,
IDockviewPanelProps,
} from 'dockview-core';
const Panel = defineComponent({
name: 'Panel',
props: {
api: {
type: Object as PropType<IDockviewPanelProps['api']>,
required: true,
},
containerApi: {
type: Object as PropType<IDockviewPanelProps['containerApi']>,
required: true,
},
params: {
type: Object as PropType<IDockviewPanelProps['params']>,
required: true,
},
},
data() {
return {
running: false,
title: '',
};
},
methods: {
onClick() {
this.running = !this.running;
},
},
watch: {
running(newValue, oldValue) {
if (!newValue) {
return;
}
console.log('interval');
const interval = setInterval(() => {
this.api.updateParameters({ myValue: Date.now() });
}, 1000);
this.api.updateParameters({ myValue: Date.now() });
return () => {
clearInterval(interval);
};
},
},
mounted() {
const disposable = this.api.onDidTitleChange(() => {
this.title = this.api.title;
});
this.title = this.api.title;
return () => {
disposable.dispose();
};
},
template: `
<div style="height:100%;padding:20px;color:white;">
<div>{{title}}</div>
<button v-if="running" @click="onClick">Stop</button>
<button v-if="!running" @click="onClick">Start</button>
<span>{{title}}</span>
</div>`,
});
interface CustomParams {
myValue: number;
}
const Tab = defineComponent({
name: 'Tab',
props: {
api: {
type: Object as PropType<
IDockviewPanelHeaderProps<CustomParams>['api']
>,
required: true,
},
containerApi: {
type: Object as PropType<
IDockviewPanelHeaderProps<CustomParams>['containerApi']
>,
required: true,
},
params: {
type: Object as PropType<
IDockviewPanelHeaderProps<CustomParams>['params']
>,
required: true,
},
},
data() {
return {
myValue: this.params.myValue,
title: '',
};
},
methods: {
onClick() {
this.running = !this.running;
},
},
watch: {
params(newValue, oldValue) {
this.myValue = newValue.myValue;
},
running(newValue, oldValue) {
if (!newValue) {
return;
}
const interval = setInterval(() => {
this.api.updateParameters({ myValue: Date.now() });
}, 1000);
this.api.updateParameters({ myValue: Date.now() });
return () => {
clearInterval(interval);
};
},
},
template: `
<div>
<div>custom tab: {{title}}</div>
<span>value: {{myValue}}</span>
</div>`,
});
const App = defineComponent({
name: 'App',
components: {
'dockview-vue': DockviewVue,
Panel,
Tab,
},
data() {
return {
components: {
default: Panel,
},
tabComponents: {
default: Tab,
},
};
},
methods: {
onReady(event: DockviewReadyEvent) {
event.api.addPanel({
id: 'panel_1',
component: 'default',
tabComponent: 'default',
params: {
myValue: Date.now(),
},
});
event.api.addPanel({
id: 'panel_2',
component: 'default',
tabComponent: 'default',
params: {
myValue: Date.now(),
},
});
},
},
template: `
<dockview-vue
style="width:100%;height:100%"
class="dockview-theme-abyss"
@ready="onReady"
:components="components"
:tabComponents="tabComponents"
</dockview-vue>`,
});
const app = createApp(App);
app.config.errorHandler = (err) => {
console.log(err);
};
app.mount(document.getElementById('app')!);

View File

@ -18,6 +18,7 @@ import {
RightControls,
} from './controls.tsx';
import { usePanelApiMetadata } from './debugPanel.tsx';
import { LogLine, LogLines } from './logLines.tsx';
const components = {
default: (props: IDockviewPanelProps) => {
@ -83,9 +84,9 @@ const colors = [
let count = 0;
const DockviewDemo = (props: { theme?: string }) => {
const [logLines, setLogLines] = React.useState<
{ text: string; timestamp?: Date; backgroundColor?: string }[]
>([]);
const [logLines, setLogLines] = React.useState<LogLine[]>([]);
const [emittedLogsOnCurrentStackFrame, setEmittedLogsOnCurrentStackFrame] =
React.useState<LogLine[]>([]);
const [panels, setPanels] = React.useState<string[]>([]);
const [groups, setGroups] = React.useState<string[]>([]);
@ -94,89 +95,100 @@ const DockviewDemo = (props: { theme?: string }) => {
const [activePanel, setActivePanel] = React.useState<string>();
const [activeGroup, setActiveGroup] = React.useState<string>();
const [pending, setPending] = React.useState<
{ text: string; timestamp?: Date }[]
>([]);
const addLogLine = (message: string) => {
setPending((line) => [
setEmittedLogsOnCurrentStackFrame((line) => [
{ text: message, timestamp: new Date() },
...line,
]);
};
React.useLayoutEffect(() => {
if (pending.length === 0) {
if (emittedLogsOnCurrentStackFrame.length === 0) {
return;
}
const color = colors[count++ % colors.length];
setLogLines((lines) => [
...pending.map((_) => ({ ..._, backgroundColor: color })),
...emittedLogsOnCurrentStackFrame.map((_) => ({
..._,
backgroundColor: color,
})),
...lines,
]);
setPending([]);
}, [pending]);
setEmittedLogsOnCurrentStackFrame([]);
}, [emittedLogsOnCurrentStackFrame]);
const onReady = (event: DockviewReadyEvent) => {
setApi(event.api);
};
event.api.onDidAddPanel((event) => {
setPanels((_) => [..._, event.id]);
addLogLine(`Panel Added ${event.id}`);
});
event.api.onDidActivePanelChange((event) => {
setActivePanel(event?.id);
addLogLine(`Panel Activated ${event?.id}`);
});
event.api.onDidRemovePanel((event) => {
setPanels((_) => {
const next = [..._];
next.splice(
next.findIndex((x) => x === event.id),
1
);
React.useEffect(() => {
if (!api) {
return;
}
return next;
});
addLogLine(`Panel Removed ${event.id}`);
});
const disposables = [
api.onDidAddPanel((event) => {
setPanels((_) => [..._, event.id]);
addLogLine(`Panel Added ${event.id}`);
}),
api.onDidActivePanelChange((event) => {
setActivePanel(event?.id);
addLogLine(`Panel Activated ${event?.id}`);
}),
api.onDidRemovePanel((event) => {
setPanels((_) => {
const next = [..._];
next.splice(
next.findIndex((x) => x === event.id),
1
);
event.api.onDidAddGroup((event) => {
setGroups((_) => [..._, event.id]);
addLogLine(`Group Added ${event.id}`);
});
return next;
});
addLogLine(`Panel Removed ${event.id}`);
}),
event.api.onDidRemoveGroup((event) => {
setGroups((_) => {
const next = [..._];
next.splice(
next.findIndex((x) => x === event.id),
1
);
api.onDidAddGroup((event) => {
setGroups((_) => [..._, event.id]);
addLogLine(`Group Added ${event.id}`);
}),
return next;
});
addLogLine(`Group Removed ${event.id}`);
});
api.onDidRemoveGroup((event) => {
setGroups((_) => {
const next = [..._];
next.splice(
next.findIndex((x) => x === event.id),
1
);
event.api.onDidActiveGroupChange((event) => {
setActiveGroup(event?.id);
addLogLine(`Group Activated ${event?.id}`);
});
return next;
});
addLogLine(`Group Removed ${event.id}`);
}),
api.onDidActiveGroupChange((event) => {
setActiveGroup(event?.id);
addLogLine(`Group Activated ${event?.id}`);
}),
];
let success = false;
const state = localStorage.getItem('dv-demo-state');
if (state) {
try {
event.api.fromJSON(JSON.parse(state));
return;
api.fromJSON(JSON.parse(state));
success = true;
} catch {
localStorage.removeItem('dv-demo-state');
}
return;
}
defaultConfig(event.api);
};
if (!success) {
defaultConfig(api);
}
return disposables.forEach((disposable) => disposable.dispose());
}, [api]);
return (
<div
@ -223,59 +235,13 @@ const DockviewDemo = (props: { theme?: string }) => {
/>
<div
style={{
// height: '200px',
width: '300px',
backgroundColor: 'black',
color: 'white',
overflow: 'auto',
}}
>
{logLines.map((line, i) => {
return (
<div
style={{
height: '30px',
overflow: 'hidden',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap',
fontSize: '13px',
display: 'flex',
alignItems: 'center',
backgroundColor: line.backgroundColor,
}}
key={i}
>
<span
style={{
display: 'inline-block',
width: '20px',
color: 'gray',
borderRight: '1px solid gray',
marginRight: '4px',
paddingLeft: '2px',
height: '100%',
}}
>
{logLines.length - i}
</span>
<span>
{line.timestamp && (
<span
style={{
fontSize: '0.7em',
padding: '0px 2px',
}}
>
{line.timestamp
.toISOString()
.substring(11, 23)}
</span>
)}
<span>{line.text}</span>
</span>
</div>
);
})}
<LogLines lines={logLines} />
</div>
</div>
</div>

View File

@ -9,9 +9,5 @@ const rootElement = document.getElementById('app');
if (rootElement) {
const root = ReactDOMClient.createRoot(rootElement);
root.render(
<div className="app">
<App />
</div>
);
root.render(<App />);
}

View File

@ -0,0 +1,54 @@
import React from 'react';
export type LogLine = {
text: string;
timestamp?: Date;
backgroundColor?: string;
};
export const LogLines = (props: { lines: LogLine[] }) => {
return props.lines.map((line, i) => {
return (
<div
style={{
height: '30px',
overflow: 'hidden',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap',
fontSize: '13px',
display: 'flex',
alignItems: 'center',
backgroundColor: line.backgroundColor,
}}
key={i}
>
<span
style={{
display: 'inline-block',
width: '20px',
color: 'gray',
borderRight: '1px solid gray',
marginRight: '4px',
paddingLeft: '2px',
height: '100%',
}}
>
{props.lines.length - i}
</span>
<span>
{line.timestamp && (
<span
style={{
fontSize: '0.7em',
padding: '0px 2px',
}}
>
{line.timestamp.toISOString().substring(11, 23)}
</span>
)}
<span>{line.text}</span>
</span>
</div>
);
});
};

View File

@ -9,9 +9,5 @@ const rootElement = document.getElementById('app');
if (rootElement) {
const root = ReactDOMClient.createRoot(rootElement);
root.render(
<div className="app">
<App />
</div>
);
root.render(<App />);
}

View File

@ -9,9 +9,5 @@ const rootElement = document.getElementById('app');
if (rootElement) {
const root = ReactDOMClient.createRoot(rootElement);
root.render(
<div className="app">
<App />
</div>
);
root.render(<App />);
}

View File

@ -26,14 +26,6 @@ const components = {
},
};
const counter = (() => {
let i = 0;
return {
next: () => ++i,
};
})();
function loadDefaultLayout(api: DockviewApi) {
api.addPanel({
id: 'panel_1',

View File

@ -9,9 +9,5 @@ const rootElement = document.getElementById('app');
if (rootElement) {
const root = ReactDOMClient.createRoot(rootElement);
root.render(
<div className="app">
<App />
</div>
);
root.render(<App />);
}

View File

@ -0,0 +1,264 @@
import 'dockview-core/dist/styles/dockview.css';
import { PropType, createApp, defineComponent } from 'vue';
import { DockviewVue } from 'dockview-vue';
import {
DockviewApi,
DockviewReadyEvent,
IDockviewHeaderActionsProps,
IDockviewPanelProps,
} from 'dockview-core';
let panelCount = 0;
const MaterialIcon = defineComponent({
name: 'MaterialIcon',
props: {
icon: {
type: String,
required: true,
},
title: {
type: String,
required: false,
},
},
emits: ['click'],
data() {
return {
title: this.title,
icon: this.icon,
};
},
methods: {
onClick() {
this.$emit('click');
},
},
template: `
<div
@click="onClick"
title="title"
style="display:flex;justify-content:center;align-items:center;width:30px;height:100%;font-size:18px;">
<span class="material-symbols-outlined" style="font-size:inherit;cursor:pointer;">
{{icon}}
</span>
</div>`,
});
const LeftAction = defineComponent({
name: 'LeftAction',
props: {
containerApi: {
type: Object as PropType<
IDockviewHeaderActionsProps['containerApi']
>,
required: true,
},
api: {
type: Object as PropType<IDockviewHeaderActionsProps['api']>,
required: true,
},
group: {
type: Object as PropType<IDockviewHeaderActionsProps['group']>,
required: true,
},
},
components: {
'material-icon': MaterialIcon,
},
methods: {
onClick() {
this.containerApi.addPanel({
id: (++panelCount).toString(),
title: `Tab ${panelCount}`,
component: 'default',
});
},
},
template: `
<div style="height:100%;color:white;padding:0px 4px;">
<material-icon @click="onClick" icon="add"></material-icon>
</div>`,
});
const RightAction = defineComponent({
name: 'RightAction',
props: {
containerApi: {
type: Object as PropType<
IDockviewHeaderActionsProps['containerApi']
>,
required: true,
},
api: {
type: Object as PropType<IDockviewHeaderActionsProps['api']>,
required: true,
},
group: {
type: Object as PropType<IDockviewHeaderActionsProps['group']>,
required: true,
},
},
components: {
'material-icon': MaterialIcon,
},
data() {
return {
floating: this.api.location.type === 'floating',
};
},
methods: {
onClick() {
if (this.floating) {
const group = this.containerApi.addGroup();
this.group.api.moveTo({ group });
} else {
this.containerApi.addFloatingGroup(this.group);
}
},
},
mounted() {
const disposable = this.group.api.onDidLocationChange((event) => {
this.floating = event.location.type === 'floating';
});
return () => {
disposable.dispose();
};
},
template: `
<div style="height:100%;color:white;padding:0px 4px">
<material-icon v-if="floating" @click="onClick" icon="jump_to_element"></material-icon>
<material-icon v-if="!floating" @click="onClick" icon="back_to_tab"></material-icon>
</div>`,
});
const Panel = defineComponent({
name: 'Panel',
props: {
api: {
type: Object as PropType<IDockviewPanelProps['api']>,
required: true,
},
containerApi: {
type: Object as PropType<IDockviewPanelProps['containerApi']>,
required: true,
},
params: {
type: Object as PropType<IDockviewPanelProps['params']>,
required: true,
},
},
data() {
return {
title: '',
};
},
mounted() {
const disposable = this.api.onDidTitleChange(() => {
this.title = this.api.title;
});
this.title = this.api.title;
return () => {
disposable.dispose();
};
},
template: `
<div style="color:white;">
<div>{{title}}</div>
</div>`,
});
const App = defineComponent({
name: 'App',
components: {
'dockview-vue': DockviewVue,
Panel,
LeftAction,
RightAction,
},
data() {
return {
components: {
default: Panel,
},
leftAction: LeftAction,
rightAction: RightAction,
api: null as DockviewApi | null,
bounds: undefined,
disableFloatingGroups: false,
};
},
methods: {
onAddFloatingGroup() {
this.api.addPanel({
id: (++panelCount).toString(),
title: `Tab ${panelCount}`,
component: 'default',
floating: { width: 250, height: 150, x: 50, y: 50 },
});
},
onToggleBounds() {
this.bounds =
this.bounds === undefined ? 'boundedWithinViewport' : undefined;
},
onToggleEnabled() {
this.disableFloatingGroups = !this.disableFloatingGroups;
},
onReady(event: DockviewReadyEvent) {
event.api.addPanel({
id: 'panel_1',
component: 'default',
title: 'Panel 1',
});
event.api.addPanel({
id: 'panel_2',
component: 'default',
title: 'Panel 2',
position: {
direction: 'right',
},
});
event.api.addPanel({
id: 'panel_3',
component: 'default',
title: 'Panel 3',
position: {
direction: 'below',
},
});
this.api = event.api;
},
},
template: `
<div style="display:flex;flex-direction:column;height:100%;">
<div style="height:25px">
<button @click="onAddFloatingGroup">Add Floating Group</button>
<button @click="onToggleBounds">{{'Bounds: ' + bounds}}</button>
<button @click="onToggleEnabled">{{'Disabled: ' + disableFloatingGroups}}</button>
</div>
<div style="flex-grow:1;">
<dockview-vue
style="width:100%;height:100%"
class="dockview-theme-abyss"
@ready="onReady"
:components="components"
:floatingGroupBounds="bounds"
:leftHeaderActionsComponent="leftAction"
:rightHeaderActionsComponent="rightAction"
:prefixHeaderActionsComponent="prefixAction"
:disableFloatingGroups="disableFloatingGroups"
</dockview-vue>
</div>
</div>`,
});
const app = createApp(App);
app.config.errorHandler = (err) => {
console.log(err);
};
app.mount(document.getElementById('app')!);

View File

@ -9,9 +9,5 @@ const rootElement = document.getElementById('app');
if (rootElement) {
const root = ReactDOMClient.createRoot(rootElement);
root.render(
<div className="app">
<App />
</div>
);
root.render(<App />);
}

View File

@ -0,0 +1,23 @@
.dockview-groupcontrol-demo {
height: 100%;
display: flex;
align-items: center;
color: white;
background-color: black;
padding: 0px 8px;
margin: 1px;
border: 1px dotted orange;
}
.dockview-groupcontrol-demo .dockview-groupcontrol-demo-group-active {
padding: 0px 8px;
}
.dockview-groupcontrol-demo .dockview-groupcontrol-demo-active-panel {
color: yellow;
padding: 0px 8px;
}

View File

@ -0,0 +1,216 @@
import 'dockview-core/dist/styles/dockview.css';
import { PropType, createApp, defineComponent } from 'vue';
import { DockviewVue } from 'dockview-vue';
import {
DockviewGroupPanelApi,
DockviewReadyEvent,
IDockviewHeaderActionsProps,
IDockviewPanelProps,
} from 'dockview-core';
import './app.css';
const LeftAction = defineComponent({
name: 'LeftAction',
props: {
containerApi: {
type: Object as PropType<
IDockviewHeaderActionsProps['containerApi']
>,
required: true,
},
api: {
type: Object as PropType<IDockviewHeaderActionsProps['api']>,
required: true,
},
group: {
type: Object as PropType<IDockviewHeaderActionsProps['group']>,
required: true,
},
},
data() {
return {
activePanel: null,
};
},
mounted() {
const disposable = this.api.onDidActivePanelChange((event) => {
this.activePanel = event.panel.id;
});
this.activePanel = this.group.activePanel.id;
return () => {
disposable.dispose();
};
},
template: `
<div class="dockview-groupcontrol-demo">
<span class="dockview-groupcontrol-demo-active-panel">
"Active Panel " {{ activePanel }}
</span>
</div>`,
});
const RightAction = defineComponent({
name: 'RightAction',
props: {
containerApi: {
type: Object as PropType<
IDockviewHeaderActionsProps['containerApi']
>,
required: true,
},
api: {
type: Object as PropType<IDockviewHeaderActionsProps['api']>,
required: true,
},
group: {
type: Object as PropType<IDockviewHeaderActionsProps['group']>,
required: true,
},
},
data() {
return {
isGroupActive: false,
};
},
mounted() {
const disposable = this.api.onDidActiveChange((event) => {
this.isGroupActive = event.isActive;
});
this.isActive = this.api.isActive;
return () => {
disposable.dispose();
};
},
template: `
<div class="dockview-groupcontrol-demo">
<span v-if="isGroupActive" style="color:green;" class="dockview-groupcontrol-demo-active-panel">
Group Active
</span>
<span v-if="!isGroupActive" style="color:red;" class="dockview-groupcontrol-demo-active-panel">
Group Inactive
</span>
</div>`,
});
const PrefixAction = defineComponent({
name: 'PrefixAction',
props: {
containerApi: {
type: Object as PropType<
IDockviewHeaderActionsProps['containerApi']
>,
required: true,
},
group: {
type: Object as PropType<IDockviewHeaderActionsProps['group']>,
required: true,
},
},
template: `<div class="dockview-groupcontrol-demo">🌲</div>`,
});
const Panel = defineComponent({
name: 'Panel',
props: {
api: {
type: Object as PropType<IDockviewPanelProps['api']>,
required: true,
},
containerApi: {
type: Object as PropType<IDockviewPanelProps['containerApi']>,
required: true,
},
params: {
type: Object as PropType<IDockviewPanelProps['params']>,
required: true,
},
},
data() {
return {
title: '',
};
},
mounted() {
const disposable = this.api.onDidTitleChange(() => {
this.title = this.api.title;
});
this.title = this.api.title;
return () => {
disposable.dispose();
};
},
template: `
<div style="color:white;">
<div>{{title}}</div>
</div>`,
});
const App = defineComponent({
name: 'App',
components: {
'dockview-vue': DockviewVue,
Panel,
LeftAction,
RightAction,
PrefixAction,
},
data() {
return {
components: {
default: Panel,
},
leftAction: LeftAction,
rightAction: RightAction,
prefixAction: PrefixAction,
};
},
methods: {
onReady(event: DockviewReadyEvent) {
event.api.addPanel({
id: 'panel_1',
component: 'default',
title: 'Panel 1',
});
event.api.addPanel({
id: 'panel_2',
component: 'default',
title: 'Panel 2',
position: {
direction: 'right',
},
});
event.api.addPanel({
id: 'panel_3',
component: 'default',
title: 'Panel 3',
position: {
direction: 'below',
},
});
},
},
template: `
<dockview-vue
style="width:100%;height:100%"
class="dockview-theme-abyss"
@ready="onReady"
:components="components"
:leftHeaderActionsComponent="leftAction"
:rightHeaderActionsComponent="rightAction"
:prefixHeaderActionsComponent="prefixAction"
</dockview-vue>`,
});
const app = createApp(App);
app.config.errorHandler = (err) => {
console.log(err);
};
app.mount(document.getElementById('app')!);

View File

@ -22,14 +22,6 @@ const components = {
},
};
const counter = (() => {
let i = 0;
return {
next: () => ++i,
};
})();
function loadDefaultLayout(api: DockviewApi) {
api.addPanel({
id: 'panel_1',

View File

@ -9,9 +9,5 @@ const rootElement = document.getElementById('app');
if (rootElement) {
const root = ReactDOMClient.createRoot(rootElement);
root.render(
<div className="app">
<App />
</div>
);
root.render(<App />);
}

View File

@ -0,0 +1,237 @@
import 'dockview-core/dist/styles/dockview.css';
import { PropType, createApp, defineComponent } from 'vue';
import { DockviewVue } from 'dockview-vue';
import {
DockviewApi,
DockviewReadyEvent,
IDockviewHeaderActionsProps,
IDockviewPanelProps,
} from 'dockview-core';
let panelCount = 0;
function loadDefaultLayout(api: DockviewApi) {
api.addPanel({
id: 'panel_1',
component: 'default',
});
api.addPanel({
id: 'panel_2',
component: 'default',
});
api.addPanel({
id: 'panel_3',
component: 'default',
});
}
const MaterialIcon = defineComponent({
name: 'MaterialIcon',
props: {
icon: {
type: String,
required: true,
},
title: {
type: String,
required: false,
},
},
emits: ['click'],
data() {
return {
title: this.title,
icon: this.icon,
};
},
methods: {
onClick() {
this.$emit('click');
},
},
template: `
<div
@click="onClick"
title="title"
style="display:flex;justify-content:center;align-items:center;width:30px;height:100%;font-size:18px;">
<span class="material-symbols-outlined" style="font-size:inherit;cursor:pointer;">
{{icon}}
</span>
</div>`,
});
const LeftAction = defineComponent({
name: 'LeftAction',
props: {
containerApi: {
type: Object as PropType<
IDockviewHeaderActionsProps['containerApi']
>,
required: true,
},
api: {
type: Object as PropType<IDockviewHeaderActionsProps['api']>,
required: true,
},
group: {
type: Object as PropType<IDockviewHeaderActionsProps['group']>,
required: true,
},
},
components: {
'material-icon': MaterialIcon,
},
methods: {
onClick() {
this.containerApi.addPanel({
id: (++panelCount).toString(),
title: `Tab ${panelCount}`,
component: 'default',
});
},
},
template: `
<div style="height:100%;color:white;padding:0px 4px;">
<material-icon @click="onClick" icon="add"></material-icon>
</div>`,
});
const Panel = defineComponent({
name: 'Panel',
props: {
api: {
type: Object as PropType<IDockviewPanelProps['api']>,
required: true,
},
containerApi: {
type: Object as PropType<IDockviewPanelProps['containerApi']>,
required: true,
},
params: {
type: Object as PropType<IDockviewPanelProps['params']>,
required: true,
},
},
data() {
return {
title: '',
};
},
mounted() {
const disposable = this.api.onDidTitleChange(() => {
this.title = this.api.title;
});
this.title = this.api.title;
return () => {
disposable.dispose();
};
},
template: `
<div style="color:white;">
<div>{{title}}</div>
</div>`,
});
const App = defineComponent({
name: 'App',
components: {
'dockview-vue': DockviewVue,
Panel,
LeftAction,
},
data() {
return {
components: {
default: Panel,
},
leftAction: LeftAction,
api: null as DockviewApi | null,
};
},
methods: {
onLoad() {
const layoutString = localStorage.getItem(
'dv-template/dockview/layout/vue'
);
let success = false;
if (layoutString) {
try {
const layout = JSON.parse(layoutString);
this.api.fromJSON(layout);
success = true;
} catch (err) {
console.error(err);
}
}
if (!success) {
loadDefaultLayout(this.api);
}
},
onSave() {
localStorage.setItem(
'dv-template/dockview/layout/vue',
JSON.stringify(this.api.toJSON())
);
},
onClear() {
localStorage.removeItem('dv-template/dockview/layout/vue');
},
onReady(event: DockviewReadyEvent) {
this.api = event.api;
this.onLoad();
},
},
watch: {
api(newValue, oldValue) {
if (!newValue) {
return;
}
const disposable = newValue.onDidLayoutChange(() => {
const layout = newValue.toJSON();
localStorage.setItem(
'dockview_persistence_layout',
JSON.stringify(layout)
);
});
return () => {
disposable.dispose();
};
},
},
template: `
<div style="display:flex;flex-direction:column;height:100%;">
<div style="height:25px">
<button @click="onLoad">Load</button>
<button @click="onSave">Save</button>
<button @click="onClear">Clear</button>
</div>
<div style="flex-grow:1;">
<dockview-vue
style="width:100%;height:100%"
class="dockview-theme-abyss"
@ready="onReady"
:components="components"
:floatingGroupBounds="bounds"
:leftHeaderActionsComponent="leftAction"
:rightHeaderActionsComponent="rightAction"
:prefixHeaderActionsComponent="prefixAction"
:disableFloatingGroups="disableFloatingGroups"
</dockview-vue>
</div>
</div>`,
});
const app = createApp(App);
app.config.errorHandler = (err) => {
console.log(err);
};
app.mount(document.getElementById('app')!);

View File

@ -9,9 +9,5 @@ const rootElement = document.getElementById('app');
if (rootElement) {
const root = ReactDOMClient.createRoot(rootElement);
root.render(
<div className="app">
<App />
</div>
);
root.render(<App />);
}

View File

@ -0,0 +1,107 @@
import 'dockview-core/dist/styles/dockview.css';
import { PropType, createApp, defineComponent } from 'vue';
import { DockviewVue } from 'dockview-vue';
import {
DockviewApi,
DockviewReadyEvent,
IDockviewPanelProps,
} from 'dockview-core';
const Panel = defineComponent({
name: 'Panel',
props: {
api: {
type: Object as PropType<IDockviewPanelProps['api']>,
required: true,
},
containerApi: {
type: Object as PropType<IDockviewPanelProps['containerApi']>,
required: true,
},
params: {
type: Object as PropType<IDockviewPanelProps['params']>,
required: true,
},
},
data() {
return {
title: '',
};
},
mounted() {
const disposable = this.api.onDidTitleChange(() => {
this.title = this.api.title;
});
this.title = this.api.title;
return () => {
disposable.dispose();
};
},
template: `
<div style="height:100%;padding:20px;">
<span style="color:white;">{{ title }}</span>
</div>`,
});
const App = defineComponent({
name: 'App',
components: {
'dockview-vue': DockviewVue,
},
data() {
return {
components: {
default: Panel,
},
};
},
methods: {
onReady(event: DockviewReadyEvent) {
event.api.addPanel({
id: 'panel_1',
component: 'default',
});
event.api.addPanel({
id: 'panel_2',
component: 'default',
position: {
direction: 'right',
referencePanel: 'panel_1',
},
});
event.api.addPanel({
id: 'panel_3',
component: 'default',
position: {
direction: 'below',
referencePanel: 'panel_1',
},
});
event.api.addPanel({
id: 'panel_4',
component: 'default',
});
event.api.addPanel({
id: 'panel_5',
component: 'default',
});
},
},
template: `
<dockview-vue
style="width:100%;height:100%"
class="dockview-theme-abyss"
@ready="onReady"
:components="components"
:locked=true
</dockview-vue>`,
});
const app = createApp(App);
app.config.errorHandler = (err) => {
console.log(err);
};
app.mount(document.getElementById('app')!);

View File

@ -7,7 +7,35 @@ import {
SerializedDockview,
} from 'dockview';
import React from 'react';
import { Icon } from './utils.tsx';
const Icon = (props: {
icon: string;
title?: string;
onClick?: (event: React.MouseEvent) => void;
}) => {
return (
<div
title={props.title}
style={{
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
width: '30px',
height: '100%',
fontSize: '18px',
}}
onClick={props.onClick}
>
<span
style={{ fontSize: 'inherit', cursor: 'pointer' }}
className="material-symbols-outlined"
>
{props.icon}
</span>
</div>
);
};
const components = {
default: (props: IDockviewPanelProps<{ title: string }>) => {
@ -25,14 +53,6 @@ const components = {
},
};
const counter = (() => {
let i = 0;
return {
next: () => ++i,
};
})();
function loadDefaultLayout(api: DockviewApi) {
api.addPanel({
id: 'panel_1',

View File

@ -9,9 +9,5 @@ const rootElement = document.getElementById('app');
if (rootElement) {
const root = ReactDOMClient.createRoot(rootElement);
root.render(
<div className="app">
<App />
</div>
);
root.render(<App />);
}

View File

@ -1,30 +0,0 @@
import React from 'react';
export const Icon = (props: {
icon: string;
title?: string;
onClick?: (event: React.MouseEvent) => void;
}) => {
return (
<div
title={props.title}
style={{
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
width: '30px',
height: '100%',
fontSize: '18px',
}}
onClick={props.onClick}
>
<span
style={{ fontSize: 'inherit', cursor: 'pointer' }}
className="material-symbols-outlined"
>
{props.icon}
</span>
</div>
);
};

View File

@ -0,0 +1,243 @@
import 'dockview-core/dist/styles/dockview.css';
import { Prop, PropType, createApp, defineComponent } from 'vue';
import { DockviewVue } from 'dockview-vue';
import {
DockviewReadyEvent,
IDockviewHeaderActionsProps,
IDockviewPanelProps,
} from 'dockview-core';
let panelCount = 0;
const MaterialIcon = defineComponent({
name: 'MaterialIcon',
props: {
icon: {
type: String,
required: true,
},
title: {
type: String,
required: false,
},
},
emits: ['click'],
data() {
return {
title: this.title,
icon: this.icon,
};
},
methods: {
onClick() {
this.$emit('click');
},
},
template: `
<div
@click="onClick"
title="title"
style="display:flex;justify-content:center;align-items:center;width:30px;height:100%;font-size:18px;">
<span class="material-symbols-outlined" style="font-size:inherit;cursor:pointer;">
{{icon}}
</span>
</div>`,
});
const LeftAction = defineComponent({
name: 'LeftAction',
props: {
containerApi: {
type: Object as PropType<
IDockviewHeaderActionsProps['containerApi']
>,
required: true,
},
group: {
type: Object as PropType<IDockviewHeaderActionsProps['group']>,
required: true,
},
},
components: {
'material-icon': MaterialIcon,
},
methods: {
onClick() {
this.containerApi.addPanel({
id: (++panelCount).toString(),
title: `Tab ${panelCount}`,
component: 'default',
position: { referenceGroup: this.group },
});
},
},
template: `
<div style="height:100%;color:white;padding:0px 4px;">
<material-icon @click="onClick" icon="add"></material-icon>
</div>`,
});
const RightAction = defineComponent({
name: 'RightAction',
props: {
containerApi: {
type: Object as PropType<
IDockviewHeaderActionsProps['containerApi']
>,
required: true,
},
api: {
type: Object as PropType<IDockviewHeaderActionsProps['api']>,
required: true,
},
group: {
type: Object as PropType<IDockviewHeaderActionsProps['group']>,
required: true,
},
},
data() {
return {
maximized: false,
};
},
mounted() {
const disposable = this.containerApi.onDidMaximizedGroupChange(() => {
this.maximized = this.api.isMaximized();
});
this.maximized = this.api.isMaximized();
return () => {
disposable.dispose();
};
},
components: {
'material-icon': MaterialIcon,
},
methods: {
onClick() {
if (this.maximized) {
this.api.exitMaximized();
} else {
this.api.maximize();
}
},
},
template: `
<div style="height:100%;color:white;padding:0px 4px;">
<material-icon v-if="maximized" @click="onClick" icon="jump_to_element" ></material-icon>
<material-icon v-if="!maximized" @click="onClick" icon="back_to_tab"></material-icon>
</div>`,
});
const Panel = defineComponent({
name: 'Panel',
props: {
api: {
type: Object as PropType<IDockviewPanelProps['api']>,
required: true,
},
containerApi: {
type: Object as PropType<IDockviewPanelProps['containerApi']>,
required: true,
},
params: {
type: Object as PropType<IDockviewPanelProps['params']>,
required: true,
},
},
data() {
return {
title: '',
};
},
mounted() {
const disposable = this.api.onDidTitleChange(() => {
this.title = this.api.title;
});
this.title = this.api.title;
return () => {
disposable.dispose();
};
},
template: `
<div style="color:white;">
<div>{{title}}</div>
</div>`,
});
const App = defineComponent({
name: 'App',
components: {
'dockview-vue': DockviewVue,
Panel,
LeftAction,
RightAction,
},
data() {
return {
components: {
default: Panel,
},
leftAction: LeftAction,
rightAction: RightAction,
};
},
methods: {
onReady(event: DockviewReadyEvent) {
event.api.addPanel({
id: 'panel_1',
component: 'default',
});
event.api.addPanel({
id: 'panel_2',
component: 'default',
position: {
direction: 'right',
referencePanel: 'panel_1',
},
});
event.api.addPanel({
id: 'panel_3',
component: 'default',
position: {
direction: 'below',
referencePanel: 'panel_1',
},
});
event.api.addPanel({
id: 'panel_4',
component: 'default',
});
event.api.addPanel({
id: 'panel_5',
component: 'default',
});
},
},
computed: {
styleObject() {
return {
height: `${this.value}%`,
width: `${this.value}%`,
};
},
},
template: `
<dockview-vue
style="width:100%;height:100%"
class="dockview-theme-abyss"
@ready="onReady"
:components="components"
:leftHeaderActionsComponent="leftAction"
:rightHeaderActionsComponent="rightAction"
</dockview-vue>`,
});
const app = createApp(App);
app.config.errorHandler = (err) => {
console.log(err);
};
app.mount(document.getElementById('app')!);

View File

@ -0,0 +1,32 @@
{
"name": "dockview.resize-container",
"description": "",
"keywords": [
"dockview"
],
"version": "1.0.0",
"main": "src/index.tsx",
"dependencies": {
"dockview": "*",
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
"devDependencies": {
"@types/react": "^18.0.28",
"@types/react-dom": "^18.0.11",
"typescript": "^4.9.5",
"react-scripts": "*"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject"
},
"browserslist": [
">0.2%",
"not dead",
"not ie <= 11",
"not op_mini all"
]
}

View File

@ -0,0 +1,94 @@
import {
DockviewDidDropEvent,
DockviewDndOverlayEvent,
DockviewReact,
DockviewReadyEvent,
IDockviewPanelProps,
} from 'dockview';
import React from 'react';
const InnerDockview = () => {
const onReady = (event: DockviewReadyEvent) => {
event.api.addPanel({
id: 'panel_1',
component: 'default',
});
event.api.addPanel({
id: 'panel_2',
component: 'default',
});
event.api.addPanel({
id: 'panel_3',
component: 'default',
});
};
return (
<DockviewReact
onReady={onReady}
components={components}
className="nested-dockview"
/>
);
};
const components = {
default: (props: IDockviewPanelProps<{ title: string }>) => {
return (
<div
style={{
height: '100%',
padding: '20px',
background: 'var(--dv-group-view-background-color)',
}}
>
{props.params.title}
</div>
);
},
innerDockview: InnerDockview,
};
const NestedDockview = (props: { theme?: string }) => {
const onReady = (event: DockviewReadyEvent) => {
event.api.addPanel({
id: 'panel_1',
component: 'default',
});
event.api.addPanel({
id: 'panel_2',
component: 'default',
});
event.api.addPanel({
id: 'panel_3',
component: 'innerDockview',
position: { referencePanel: 'panel_2', direction: 'right' },
});
};
const showDndOverlay = (event: DockviewDndOverlayEvent) => {
// console.log(event.getData());
return false;
};
const onDidDrop = (event: DockviewDidDropEvent) => {
// event.getData();
};
return (
<DockviewReact
onReady={onReady}
components={components}
className={`${props.theme || 'dockview-theme-abyss'}`}
showDndOverlay={showDndOverlay}
onDidDrop={onDidDrop}
/>
);
};
export default NestedDockview;

View File

@ -0,0 +1,13 @@
import React from 'react';
import ReactDOMClient from 'react-dom/client';
import 'dockview/dist/styles/dockview.css';
import App from './app.tsx';
const rootElement = document.getElementById('app');
if (rootElement) {
const root = ReactDOMClient.createRoot(rootElement);
root.render(<App />);
}

View File

@ -0,0 +1,18 @@
{
"compilerOptions": {
"outDir": "build/dist",
"module": "esnext",
"target": "es5",
"lib": ["es6", "dom"],
"sourceMap": true,
"allowJs": true,
"jsx": "react-jsx",
"moduleResolution": "node",
"rootDir": "src",
"forceConsistentCasingInFileNames": true,
"noImplicitReturns": true,
"noImplicitThis": true,
"noImplicitAny": true,
"strictNullChecks": true
}
}

View File

@ -0,0 +1,131 @@
import 'dockview-core/dist/styles/dockview.css';
import { PropType, createApp, defineComponent } from 'vue';
import { DockviewVue } from 'dockview-vue';
import { DockviewReadyEvent, IDockviewPanelProps } from 'dockview-core';
const Panel = defineComponent({
name: 'Panel',
props: {
api: {
type: Object as PropType<IDockviewPanelProps['api']>,
required: true,
},
containerApi: {
type: Object as PropType<IDockviewPanelProps['containerApi']>,
required: true,
},
params: {
type: Object as PropType<IDockviewPanelProps['params']>,
required: true,
},
},
data() {
return {
title: '',
};
},
mounted() {
const disposable = this.api.onDidTitleChange(() => {
this.title = this.api.title;
});
this.title = this.api.title;
return () => {
disposable.dispose();
};
},
template: `
<div style="height:100%;padding:20px;color:white;">
<div>{{title}}</div>
</div>`,
});
const InnerDockview = defineComponent({
name: 'App',
components: {
'dockview-vue': DockviewVue,
Panel,
},
data() {
return {
components: {
default: Panel,
innerDockview: InnerDockview,
},
};
},
methods: {
onReady(event: DockviewReadyEvent) {
event.api.addPanel({
id: 'panel_1',
component: 'default',
});
event.api.addPanel({
id: 'panel_2',
component: 'default',
});
event.api.addPanel({
id: 'panel_3',
component: 'default',
});
},
},
template: `
<dockview-vue
style="width:100%;height:100%"
class="dockview-theme-abyss"
:components="components"
@ready="onReady"
</dockview-vue>`,
});
const App = defineComponent({
name: 'App',
components: {
'dockview-vue': DockviewVue,
Panel,
InnerDockview,
},
data() {
return {
components: {
default: Panel,
innerDockview: InnerDockview,
},
};
},
methods: {
onReady(event: DockviewReadyEvent) {
event.api.addPanel({
id: 'panel_1',
component: 'default',
});
event.api.addPanel({
id: 'panel_2',
component: 'default',
});
event.api.addPanel({
id: 'panel_3',
component: 'innerDockview',
position: { referencePanel: 'panel_2', direction: 'right' },
});
},
},
template: `
<dockview-vue
style="width:100%;height:100%"
class="dockview-theme-abyss"
:components="components"
@ready="onReady"
</dockview-vue>`,
});
const app = createApp(App);
app.config.errorHandler = (err) => {
console.log(err);
};
app.mount(document.getElementById('app')!);

View File

@ -9,9 +9,5 @@ const rootElement = document.getElementById('app');
if (rootElement) {
const root = ReactDOMClient.createRoot(rootElement);
root.render(
<div className="app">
<App />
</div>
);
root.render(<App />);
}

View File

@ -0,0 +1,266 @@
import 'dockview-core/dist/styles/dockview.css';
import { PropType, createApp, defineComponent } from 'vue';
import { DockviewVue } from 'dockview-vue';
import {
DockviewApi,
DockviewReadyEvent,
IDockviewHeaderActionsProps,
IDockviewPanelProps,
} from 'dockview-core';
let panelCount = 0;
const MaterialIcon = defineComponent({
name: 'MaterialIcon',
props: {
icon: {
type: String,
required: true,
},
title: {
type: String,
required: false,
},
},
emits: ['click'],
data() {
return {
title: this.title,
icon: this.icon,
};
},
methods: {
onClick() {
this.$emit('click');
},
},
template: `
<div
@click="onClick"
title="title"
style="display:flex;justify-content:center;align-items:center;width:30px;height:100%;font-size:18px;">
<span class="material-symbols-outlined" style="font-size:inherit;cursor:pointer;">
{{icon}}
</span>
</div>`,
});
const LeftAction = defineComponent({
name: 'LeftAction',
props: {
containerApi: {
type: Object as PropType<
IDockviewHeaderActionsProps['containerApi']
>,
required: true,
},
api: {
type: Object as PropType<IDockviewHeaderActionsProps['api']>,
required: true,
},
group: {
type: Object as PropType<IDockviewHeaderActionsProps['group']>,
required: true,
},
},
components: {
'material-icon': MaterialIcon,
},
methods: {
onClick() {
this.containerApi.addPanel({
id: (++panelCount).toString(),
title: `Tab ${panelCount}`,
component: 'default',
});
},
},
template: `
<div style="height:100%;color:white;padding:0px 4px;">
<material-icon @click="onClick" icon="add"></material-icon>
</div>`,
});
const RightAction = defineComponent({
name: 'RightAction',
props: {
containerApi: {
type: Object as PropType<
IDockviewHeaderActionsProps['containerApi']
>,
required: true,
},
api: {
type: Object as PropType<IDockviewHeaderActionsProps['api']>,
required: true,
},
group: {
type: Object as PropType<IDockviewHeaderActionsProps['group']>,
required: true,
},
},
components: {
'material-icon': MaterialIcon,
},
data() {
return {
isPopout: this.api.location.type === 'popout',
};
},
methods: {
onClick() {
if (this.isPopout) {
const group = this.containerApi.addGroup();
this.group.api.moveTo({ group });
} else {
this.containerApi.addPopoutGroup(this.group, {
popoutUrl: '/popout/index.html',
});
}
},
},
mounted() {
const disposable = this.group.api.onDidLocationChange((event) => {
this.isPopout = event.location.type === 'popout';
});
return () => {
disposable.dispose();
};
},
template: `
<div style="height:100%;color:white;padding:0px 4px">
<material-icon v-if="isPopout" @click="onClick" icon="jump_to_element"></material-icon>
<material-icon v-if="!isPopout" @click="onClick" icon="back_to_tab"></material-icon>
</div>`,
});
const Panel = defineComponent({
name: 'Panel',
props: {
api: {
type: Object as PropType<IDockviewPanelProps['api']>,
required: true,
},
containerApi: {
type: Object as PropType<IDockviewPanelProps['containerApi']>,
required: true,
},
params: {
type: Object as PropType<IDockviewPanelProps['params']>,
required: true,
},
},
data() {
return {
title: '',
};
},
mounted() {
const disposable = this.api.onDidTitleChange(() => {
this.title = this.api.title;
});
this.title = this.api.title;
return () => {
disposable.dispose();
};
},
template: `
<div style="color:white;">
<div>{{title}}</div>
</div>`,
});
const App = defineComponent({
name: 'App',
components: {
'dockview-vue': DockviewVue,
Panel,
LeftAction,
RightAction,
},
data() {
return {
components: {
default: Panel,
},
leftAction: LeftAction,
rightAction: RightAction,
api: null as DockviewApi | null,
bounds: undefined,
disableFloatingGroups: false,
};
},
methods: {
onAddFloatingGroup() {
this.api.addPanel({
id: (++panelCount).toString(),
title: `Tab ${panelCount}`,
component: 'default',
floating: { width: 250, height: 150, x: 50, y: 50 },
});
},
onToggleBounds() {
this.bounds =
this.bounds === undefined ? 'boundedWithinViewport' : undefined;
},
onToggleEnabled() {
this.disableFloatingGroups = !this.disableFloatingGroups;
},
onReady(event: DockviewReadyEvent) {
event.api.addPanel({
id: 'panel_1',
component: 'default',
title: 'Panel 1',
});
event.api.addPanel({
id: 'panel_2',
component: 'default',
title: 'Panel 2',
position: {
direction: 'right',
},
});
event.api.addPanel({
id: 'panel_3',
component: 'default',
title: 'Panel 3',
position: {
direction: 'below',
},
});
this.api = event.api;
},
},
template: `
<div style="display:flex;flex-direction:column;height:100%;">
<div style="height:25px">
<button @click="onAddFloatingGroup">Add Floating Group</button>
<button @click="onToggleBounds">{{'Bounds: ' + bounds}}</button>
<button @click="onToggleEnabled">{{'Disabled: ' + disableFloatingGroups}}</button>
</div>
<div style="flex-grow:1;">
<dockview-vue
style="width:100%;height:100%"
class="dockview-theme-abyss"
@ready="onReady"
:components="components"
:floatingGroupBounds="bounds"
:leftHeaderActionsComponent="leftAction"
:rightHeaderActionsComponent="rightAction"
:prefixHeaderActionsComponent="prefixAction"
:disableFloatingGroups="disableFloatingGroups"
</dockview-vue>
</div>
</div>`,
});
const app = createApp(App);
app.config.errorHandler = (err) => {
console.log(err);
};
app.mount(document.getElementById('app')!);

View File

@ -48,15 +48,14 @@ const components = {
}}
>
<div>{props.api.title}</div>
<input />
<div>
{mode}
<button
onClick={() => {
setMode(
mode === 'onlyWhenVisibile'
mode === 'onlyWhenVisible'
? 'always'
: 'onlyWhenVisibile'
: 'onlyWhenVisible'
);
}}
>

View File

@ -9,9 +9,5 @@ const rootElement = document.getElementById('app');
if (rootElement) {
const root = ReactDOMClient.createRoot(rootElement);
root.render(
<div className="app">
<App />
</div>
);
root.render(<App />);
}

View File

@ -0,0 +1,115 @@
import 'dockview-core/dist/styles/dockview.css';
import { PropType, createApp, defineComponent } from 'vue';
import { DockviewVue } from 'dockview-vue';
import { DockviewReadyEvent, IDockviewPanelProps } from 'dockview-core';
const Panel = defineComponent({
name: 'Panel',
props: {
api: {
type: Object as PropType<IDockviewPanelProps['api']>,
required: true,
},
containerApi: {
type: Object as PropType<IDockviewPanelProps['containerApi']>,
required: true,
},
params: {
type: Object as PropType<IDockviewPanelProps['params']>,
required: true,
},
},
data() {
return {
title: '',
renderer: null,
};
},
methods: {
onToggleRenderMode() {
this.api.setRenderer(
this.api.renderer === 'onlyWhenVisible'
? 'always'
: 'onlyWhenVisible'
);
},
},
mounted() {
const disposable = this.api.onDidTitleChange(() => {
this.title = this.api.title;
});
const disposable2 = this.api.onDidRendererChange((event) => {
this.renderer = event.renderer;
});
this.title = this.api.title;
this.renderer = this.api.renderer;
return () => {
disposable.dispose();
disposable2.dispose();
};
},
template: `
<div style="height:100%;color:white;">
<div>{{title}}</div>
<button @click="onToggleRenderMode">{{renderer}}</button>
</div>`,
});
const App = defineComponent({
name: 'App',
components: {
'dockview-vue': DockviewVue,
Panel,
},
data() {
return {
components: {
default: Panel,
},
};
},
methods: {
onReady(event: DockviewReadyEvent) {
event.api.addPanel({
id: 'panel_1',
component: 'default',
title: 'Panel 1',
});
event.api.addPanel({
id: 'panel_2',
component: 'default',
title: 'Panel 2',
position: { referencePanel: 'panel_1', direction: 'within' },
});
event.api.addPanel({
id: 'panel_3',
component: 'default',
title: 'Panel 3',
});
event.api.addPanel({
id: 'panel_4',
component: 'default',
title: 'Panel 4',
position: { referencePanel: 'panel_3', direction: 'below' },
});
},
},
template: `
<dockview-vue
style="width:100%;height:100%"
class="dockview-theme-abyss"
@ready="onReady"
:components="components"
</dockview-vue>`,
});
const app = createApp(App);
app.config.errorHandler = (err) => {
console.log(err);
};
app.mount(document.getElementById('app')!);

View File

@ -53,7 +53,7 @@ export const App: React.FC = (props: { theme?: string }) => {
position: { referencePanel: 'panel_1', direction: 'right' },
});
const panel5 = event.api.addPanel({
event.api.addPanel({
id: 'panel_5',
component: 'default',
params: {
@ -62,9 +62,6 @@ export const App: React.FC = (props: { theme?: string }) => {
position: { referencePanel: 'panel_3', direction: 'right' },
});
// panel5.group!.model.header.hidden = true;
// panel5.group!.model.locked = true;
event.api.addPanel({
id: 'panel_6',
component: 'default',

View File

@ -9,9 +9,5 @@ const rootElement = document.getElementById('app');
if (rootElement) {
const root = ReactDOMClient.createRoot(rootElement);
root.render(
<div className="app">
<App />
</div>
);
root.render(<App />);
}

View File

@ -0,0 +1,150 @@
import 'dockview-core/dist/styles/dockview.css';
import { PropType, createApp, defineComponent } from 'vue';
import { DockviewVue } from 'dockview-vue';
import { DockviewReadyEvent, IDockviewPanelProps } from 'dockview-core';
const Panel = defineComponent({
name: 'Panel',
props: {
api: {
type: Object as PropType<IDockviewPanelProps['api']>,
required: true,
},
containerApi: {
type: Object as PropType<IDockviewPanelProps['containerApi']>,
required: true,
},
params: {
type: Object as PropType<IDockviewPanelProps['params']>,
required: true,
},
},
data() {
return {
title: '',
};
},
mounted() {
const disposable = this.api.onDidTitleChange(() => {
this.title = this.api.title;
});
this.title = this.api.title;
return () => {
disposable.dispose();
};
},
template: `
<div style="height:100%;padding:20px;color:white;">
<div>{{title}}</div>
</div>`,
});
const App = defineComponent({
name: 'App',
components: {
'dockview-vue': DockviewVue,
Panel,
},
data() {
return {
components: {
default: Panel,
},
value: 50,
};
},
methods: {
onReady(event: DockviewReadyEvent) {
const panel = event.api.addPanel({
id: 'panel_1',
component: 'default',
params: {
title: 'Panel 1',
},
});
panel.group.locked = true;
panel.group.header.hidden = true;
event.api.addPanel({
id: 'panel_2',
component: 'default',
params: {
title: 'Panel 2',
},
});
event.api.addPanel({
id: 'panel_3',
component: 'default',
params: {
title: 'Panel 3',
},
});
event.api.addPanel({
id: 'panel_4',
component: 'default',
params: {
title: 'Panel 4',
},
position: { referencePanel: 'panel_1', direction: 'right' },
});
event.api.addPanel({
id: 'panel_5',
component: 'default',
params: {
title: 'Panel 5',
},
position: { referencePanel: 'panel_3', direction: 'right' },
});
event.api.addPanel({
id: 'panel_6',
component: 'default',
params: {
title: 'Panel 6',
},
position: { referencePanel: 'panel_5', direction: 'below' },
});
event.api.addPanel({
id: 'panel_7',
component: 'default',
params: {
title: 'Panel 7',
},
position: { referencePanel: 'panel_6', direction: 'right' },
});
},
},
computed: {
styleObject() {
return {
height: `${this.value}%`,
width: `${this.value}%`,
};
},
},
template: `
<div style="height:100%;display:flex;flex-direction:column;">
<input v-model="value" type="range" min="1" max="100"></input>
<div :style="styleObject">
<dockview-vue
style="width:100%;height:100%"
class="dockview-theme-abyss"
@ready="onReady"
:components="components"
:disableFloatingGroups=true
</dockview-vue>
</div>
</div>`,
});
const app = createApp(App);
app.config.errorHandler = (err) => {
console.log(err);
};
app.mount(document.getElementById('app')!);

View File

@ -9,9 +9,5 @@ const rootElement = document.getElementById('app');
if (rootElement) {
const root = ReactDOMClient.createRoot(rootElement);
root.render(
<div className="app">
<App />
</div>
);
root.render(<App />);
}

View File

@ -0,0 +1,138 @@
import 'dockview-core/dist/styles/dockview.css';
import { PropType, createApp, defineComponent } from 'vue';
import { DockviewVue } from 'dockview-vue';
import { DockviewReadyEvent, IDockviewPanelProps } from 'dockview-core';
import './resize.css';
const Panel = defineComponent({
name: 'Panel',
props: {
api: {
type: Object as PropType<IDockviewPanelProps['api']>,
required: true,
},
containerApi: {
type: Object as PropType<IDockviewPanelProps['containerApi']>,
required: true,
},
params: {
type: Object as PropType<IDockviewPanelProps['params']>,
required: true,
},
},
data() {
return {
title: '',
width: 100,
height: 100,
};
},
methods: {
onResizeGroupWidth() {
this.api.group.api.setSize({ width: this.width });
},
onResizePanelWidth() {
this.api.setSize({ width: this.width });
},
onResizeGroupHeight() {
this.api.group.api.setSize({ height: this.height });
},
onResizePanelHeight() {
this.api.setSize({ height: this.height });
},
},
mounted() {
const disposable = this.api.onDidTitleChange(() => {
this.title = this.api.title;
});
this.title = this.api.title;
return () => {
disposable.dispose();
};
},
template: `
<div class="resize-panel">
<div style="height:25px;">{{title}}</div>
<div class="resize-control">
<span>Width:</span>
<input v-model="width" type="number" min="50" step="1"></input>
<button @click="onResizeGroupWidth">Resize Group</button>
<button @click="onResizePanelWidth">Resize Panel</button>
</div>
<div class="resize-control">
<span>Height:</span>
<input v-model="height" type="number" min="50" step="1"></input>
<button @click="onResizeGroupHeight">Resize Group</button>
<button @click="onResizePanelHeight">Resize Panel</button>
</div>
</div>`,
});
const App = defineComponent({
name: 'App',
components: {
'dockview-vue': DockviewVue,
Panel,
},
data() {
return {
components: {
default: Panel,
},
};
},
methods: {
onReady(event: DockviewReadyEvent) {
event.api.addPanel({
id: 'panel_1',
component: 'default',
});
event.api.addPanel({
id: 'panel_2',
component: 'default',
position: {
direction: 'right',
referencePanel: 'panel_1',
},
});
event.api.addPanel({
id: 'panel_3',
component: 'default',
position: {
direction: 'below',
referencePanel: 'panel_1',
},
});
event.api.addPanel({
id: 'panel_4',
component: 'default',
});
event.api.addPanel({
id: 'panel_5',
component: 'default',
});
},
},
computed: {
styleObject() {
return {
height: `${this.value}%`,
width: `${this.value}%`,
};
},
},
template: `
<dockview-vue
style="width:100%;height:100%"
class="dockview-theme-abyss"
@ready="onReady"
:components="components"
</dockview-vue>`,
});
const app = createApp(App);
app.config.errorHandler = (err) => {
console.log(err);
};
app.mount(document.getElementById('app')!);

View File

@ -0,0 +1,23 @@
.resize-panel {
padding: 10px;
color: white;
}
.resize-panel .resize-control {
display: flex;
height: 18px;
line-height: 18px;
font-size: 13px;
span {
width: 60px;
}
input {
width: 75px;
}
button {
width: 50px;
}
}

View File

@ -1,15 +0,0 @@
.group-control .action {
padding: 4px;
display: flex;
align-items: center;
justify-content: center;
box-sizing: border-box;
font-size: 18px;
cursor: pointer;
}
.group-control .action:hover {
border-radius: 2px;
background-color: var(--dv-icon-hover-background-color);
}

View File

@ -4,7 +4,6 @@ import {
DockviewReadyEvent,
IDockviewPanelProps,
} from 'dockview';
import './app.css';
const TEXT =
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.';

View File

@ -9,9 +9,5 @@ const rootElement = document.getElementById('app');
if (rootElement) {
const root = ReactDOMClient.createRoot(rootElement);
root.render(
<div className="app">
<App />
</div>
);
root.render(<App />);
}

View File

@ -0,0 +1,101 @@
import 'dockview-core/dist/styles/dockview.css';
import { createApp, defineComponent } from 'vue';
import { DockviewVue } from 'dockview-vue';
import { DockviewReadyEvent } from 'dockview-core';
const TEXT =
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.';
const FixedHeightContainer = defineComponent({
name: 'FixedHeightContainer',
data() {
return {
text: [TEXT, '\n\n'].join('').repeat(20),
};
},
template: `
<div style="height:100%;color:white;">
{{text}}
</div>`,
});
const OverflowContainer = defineComponent({
name: 'OverflowContainer',
data() {
return {
text: [TEXT, '\n\n'].join('').repeat(20),
};
},
template: `
<div style="height:200px;overflow:auto;color:white;">
{{text}}
</div>`,
});
const UserDefinedOverflowContainer = defineComponent({
name: 'UserDefinedOverflowContainer',
data() {
return {
text: [TEXT, '\n\n'].join('').repeat(20),
};
},
template: `
<div style="height:100%;color:white;">
<div style="height:100%;overflow:auto;">
{{text}}
</div>
</div>`,
});
const App = defineComponent({
name: 'App',
components: {
'dockview-vue': DockviewVue,
},
data() {
return {
components: {
fixedHeightContainer: FixedHeightContainer,
overflowContainer: OverflowContainer,
userDefinedOverflowContainer: UserDefinedOverflowContainer,
},
};
},
methods: {
onReady(event: DockviewReadyEvent) {
event.api.addPanel({
id: 'panel_1',
component: 'fixedHeightContainer',
title: 'Panel 1',
});
event.api.addPanel({
id: 'panel_2',
component: 'overflowContainer',
title: 'Panel 2',
position: { direction: 'right' },
});
event.api.addPanel({
id: 'panel_3',
component: 'userDefinedOverflowContainer',
title: 'Panel 3',
position: { direction: 'right' },
});
},
},
template: `
<dockview-vue
style="width:100%;height:100%"
class="dockview-theme-abyss"
@ready="onReady"
:components="components"
:disableFloatingGroups=true
</dockview-vue>`,
});
const app = createApp(App);
app.config.errorHandler = (err) => {
console.log(err);
};
app.mount(document.getElementById('app')!);

View File

@ -9,9 +9,5 @@ const rootElement = document.getElementById('app');
if (rootElement) {
const root = ReactDOMClient.createRoot(rootElement);
root.render(
<div className="app">
<App />
</div>
);
root.render(<App />);
}

View File

@ -84,12 +84,10 @@ const App = defineComponent({
id: 'panel_1',
component: 'default',
});
event.api.addPanel({
id: 'panel_2',
component: 'default',
});
event.api.addPanel({
id: 'panel_3',
component: 'default',
@ -102,8 +100,6 @@ const App = defineComponent({
id: 'panel_5',
component: 'default',
});
this.api = event.api;
},
},
template: `

View File

@ -29,7 +29,7 @@ const components = {
}, [running]);
return (
<div style={{ height: '100%', padding: '20px', color: 'white' }}>
<div style={{ padding: '20px', color: 'white' }}>
<div>{props.api.title}</div>
<button onClick={() => setRunning(!running)}>
{running ? 'Stop' : 'Start'}

View File

@ -9,9 +9,5 @@ const rootElement = document.getElementById('app');
if (rootElement) {
const root = ReactDOMClient.createRoot(rootElement);
root.render(
<div className="app">
<App />
</div>
);
root.render(<App />);
}

View File

@ -25,31 +25,13 @@ const Panel = defineComponent({
},
data() {
return {
running: false,
title: '',
isRunning: false,
title: null,
};
},
methods: {
onClick() {
this.running = !this.running;
},
},
watch: {
running(newValue, oldValue) {
if (!newValue) {
return;
}
console.log('interval');
const interval = setInterval(() => {
this.api.updateParameters({ myValue: Date.now() });
}, 1000);
this.api.updateParameters({ myValue: Date.now() });
return () => {
clearInterval(interval);
};
toggle() {
this.isRunning = !this.isRunning;
},
},
mounted() {
@ -62,57 +44,8 @@ const Panel = defineComponent({
disposable.dispose();
};
},
template: `
<div style="height:100%;padding:20px;color:white;">
<div>{{title}}</div>
<button v-if="running" @click="onClick">Stop</button>
<button v-if="!running" @click="onClick">Start</button>
<span>{{title}}</span>
</div>`,
});
interface CustomParams {
myValue: number;
}
const Tab = defineComponent({
name: 'Tab',
props: {
api: {
type: Object as PropType<
IDockviewPanelHeaderProps<CustomParams>['api']
>,
required: true,
},
containerApi: {
type: Object as PropType<
IDockviewPanelHeaderProps<CustomParams>['containerApi']
>,
required: true,
},
params: {
type: Object as PropType<
IDockviewPanelHeaderProps<CustomParams>['params']
>,
required: true,
},
},
data() {
return {
myValue: this.params.myValue,
title: '',
};
},
methods: {
onClick() {
this.running = !this.running;
},
},
watch: {
params(newValue, oldValue) {
this.myValue = newValue.myValue;
},
running(newValue, oldValue) {
isRunning(newValue, oldValue) {
if (!newValue) {
return;
}
@ -127,22 +60,67 @@ const Tab = defineComponent({
};
},
},
template: `
<div>
<div>custom tab: {{title}}</div>
<span>value: {{myValue}}</span>
<div style="height:100%;color:white;">
<div>{{title}}</div>
<button v-if="!isRunning" @click="toggle">Start</button>
<button v-if="isRunning" @click="toggle">Stop</button>
</div>`,
});
const Tab = defineComponent({
name: 'Tab',
props: {
api: {
type: Object as PropType<IDockviewPanelHeaderProps['api']>,
required: true,
},
containerApi: {
type: Object as PropType<IDockviewPanelHeaderProps['containerApi']>,
required: true,
},
params: {
type: Object as PropType<IDockviewPanelHeaderProps['params']>,
required: true,
},
},
data() {
return {
title: '',
value: null,
};
},
mounted() {
const disposable = this.api.onDidTitleChange(() => {
this.title = this.api.title;
});
const disposable2 = this.api.onDidParametersChange(() => {
this.value = this.params.myValue;
});
this.title = this.api.title;
this.value = this.params.myValue;
return () => {
disposable.dispose();
disposable2.dispose();
};
},
template: `
<div>
<div>custom tab: {{title}}</div>
<div>value: {{value}}</div>
</div>`,
});
const App = defineComponent({
name: 'App',
components: {
'dockview-vue': DockviewVue,
Panel,
Tab,
},
setup() {
data() {
return {
components: {
default: Panel,

View File

@ -9,9 +9,5 @@ const rootElement = document.getElementById('app');
if (rootElement) {
const root = ReactDOMClient.createRoot(rootElement);
root.render(
<div className="app">
<App />
</div>
);
root.render(<App />);
}

View File

@ -1,7 +1,10 @@
import 'dockview-core/dist/styles/dockview.css';
import { PropType, createApp, defineComponent } from 'vue';
import { DockviewVue } from 'dockview-vue';
import { DockviewReadyEvent, IDockviewPanelProps } from 'dockview-core';
import {
DockviewReadyEvent,
IDockviewPanelProps,
} from 'dockview-core';
const Panel = defineComponent({
name: 'Panel',
@ -61,7 +64,7 @@ const App = defineComponent({
'dockview-vue': DockviewVue,
Panel,
},
setup() {
data() {
return {
components: {
default: Panel,

View File

@ -9,9 +9,5 @@ const rootElement = document.getElementById('app');
if (rootElement) {
const root = ReactDOMClient.createRoot(rootElement);
root.render(
<div className="app">
<App />
</div>
);
root.render(<App />);
}

View File

@ -83,9 +83,6 @@ const App = defineComponent({
Panel,
},
data() {
return { api: null as DockviewApi | null };
},
setup() {
return {
components: {
default: Panel,
@ -102,8 +99,6 @@ const App = defineComponent({
this.api.addGroup();
},
onReady(event: DockviewReadyEvent) {
console.log('ready');
event.api.fromJSON({
grid: {
orientation: Orientation.HORIZONTAL,
@ -113,8 +108,6 @@ const App = defineComponent({
},
panels: {},
});
this.api = event.api;
},
},
template: `

105
yarn.lock
View File

@ -1961,6 +1961,13 @@
dependencies:
regenerator-runtime "^0.14.0"
"@babel/runtime@^7.21.0":
version "7.24.4"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.24.4.tgz#de795accd698007a66ba44add6cc86542aff1edd"
integrity sha512-dkxf7+hn8mFBwKjs9bvBlArzLVxVbS8usaPUDd5p2a9JCL9tB8OaOVN1isD4+Xyk4ns89/xeOmbQvgdK7IIVdA==
dependencies:
regenerator-runtime "^0.14.0"
"@babel/runtime@^7.22.6":
version "7.23.7"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.23.7.tgz#dd7c88deeb218a0f8bd34d5db1aa242e0f203193"
@ -2338,7 +2345,7 @@
"@docusaurus/theme-search-algolia" "3.1.1"
"@docusaurus/types" "3.1.1"
"@docusaurus/react-loadable@5.5.2", "react-loadable@npm:@docusaurus/react-loadable@5.5.2":
"@docusaurus/react-loadable@5.5.2":
version "5.5.2"
resolved "https://registry.yarnpkg.com/@docusaurus/react-loadable/-/react-loadable-5.5.2.tgz#81aae0db81ecafbdaee3651f12804580868fa6ce"
integrity sha512-A3dYjdBGuy0IGT+wyLIGIKLRE+sAk1iNk0f1HjNDysO7u8lhL4N3VEm+FAubmJbAztn94F7MxBTPmnixbiyFdQ==
@ -6593,6 +6600,21 @@ concat-with-sourcemaps@^1.0.0, concat-with-sourcemaps@^1.1.0:
dependencies:
source-map "^0.6.1"
concurrently@^8.2.2:
version "8.2.2"
resolved "https://registry.yarnpkg.com/concurrently/-/concurrently-8.2.2.tgz#353141985c198cfa5e4a3ef90082c336b5851784"
integrity sha512-1dP4gpXFhei8IOtlXRE/T/4H88ElHgTiUzh71YUmtjTEHMSRS2Z/fgOxHSxxusGHogsRfxNq1vyAwxSC+EVyDg==
dependencies:
chalk "^4.1.2"
date-fns "^2.30.0"
lodash "^4.17.21"
rxjs "^7.8.1"
shell-quote "^1.8.1"
spawn-command "0.0.2"
supports-color "^8.1.1"
tree-kill "^1.2.2"
yargs "^17.7.2"
config-chain@^1.1.11:
version "1.1.13"
resolved "https://registry.yarnpkg.com/config-chain/-/config-chain-1.1.13.tgz#fad0795aa6a6cdaff9ed1b68e9dff94372c232f4"
@ -7077,6 +7099,13 @@ data-urls@^5.0.0:
whatwg-mimetype "^4.0.0"
whatwg-url "^14.0.0"
date-fns@^2.30.0:
version "2.30.0"
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.30.0.tgz#f367e644839ff57894ec6ac480de40cae4b0f4d0"
integrity sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==
dependencies:
"@babel/runtime" "^7.21.0"
dateformat@^3.0.3:
version "3.0.3"
resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae"
@ -9200,11 +9229,6 @@ has-yarn@^3.0.0:
resolved "https://registry.yarnpkg.com/has-yarn/-/has-yarn-3.0.0.tgz#c3c21e559730d1d3b57e28af1f30d06fac38147d"
integrity sha512-IrsVwUHhEULx3R8f/aA8AHuEzAorplsab/v8HBzEiIukwq5i/EC+xmOW+HfP1OaDP+2JkgT1yILHN2O3UFIbcA==
hash-sum@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/hash-sum/-/hash-sum-2.0.0.tgz#81d01bb5de8ea4a214ad5d6ead1b523460b0b45a"
integrity sha512-WdZTbAByD+pHfl/g9QSsBIIwy8IT+EsPiKDs0KNX+zSHhdDLFKdZu0BQHljvO+0QI/BasbMSUa8wYNCZTvhslg==
hasown@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.0.tgz#f4c513d454a57b7c7e1650778de226b11700546c"
@ -14311,6 +14335,14 @@ react-loadable-ssr-addon-v5-slorber@^1.0.1:
dependencies:
"@babel/runtime" "^7.10.3"
"react-loadable@npm:@docusaurus/react-loadable@5.5.2":
version "5.5.2"
resolved "https://registry.yarnpkg.com/@docusaurus/react-loadable/-/react-loadable-5.5.2.tgz#81aae0db81ecafbdaee3651f12804580868fa6ce"
integrity sha512-A3dYjdBGuy0IGT+wyLIGIKLRE+sAk1iNk0f1HjNDysO7u8lhL4N3VEm+FAubmJbAztn94F7MxBTPmnixbiyFdQ==
dependencies:
"@types/react" "*"
prop-types "^15.6.2"
react-remove-scroll-bar@^2.3.3:
version "2.3.4"
resolved "https://registry.yarnpkg.com/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.4.tgz#53e272d7a5cb8242990c7f144c44d8bd8ab5afd9"
@ -14947,15 +14979,6 @@ rollup-plugin-postcss@^4.0.2:
safe-identifier "^0.4.2"
style-inject "^0.3.0"
rollup-plugin-vue@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/rollup-plugin-vue/-/rollup-plugin-vue-6.0.0.tgz#e379e93e5ae9a8648522f698be2e452e8672aaf2"
integrity sha512-oVvUd84d5u73M2HYM3XsMDLtZRIA/tw2U0dmHlXU2UWP5JARYHzh/U9vcxaN/x/9MrepY7VH3pHFeOhrWpxs/Q==
dependencies:
debug "^4.1.1"
hash-sum "^2.0.0"
rollup-pluginutils "^2.8.2"
rollup-pluginutils@^2.8.2:
version "2.8.2"
resolved "https://registry.yarnpkg.com/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz#72f2af0748b592364dbd3389e600e5a9444a351e"
@ -15037,7 +15060,7 @@ run-parallel@^1.1.9:
dependencies:
queue-microtask "^1.2.2"
rxjs@^7.5.5:
rxjs@^7.5.5, rxjs@^7.8.1:
version "7.8.1"
resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.1.tgz#6f6f3d99ea8044291efd92e7c7fcf562c4057543"
integrity sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==
@ -15589,6 +15612,11 @@ sparkles@^1.0.0:
resolved "https://registry.yarnpkg.com/sparkles/-/sparkles-1.0.1.tgz#008db65edce6c50eec0c5e228e1945061dd0437c"
integrity sha512-dSO0DDYUahUt/0/pD/Is3VIm5TGJjludZ0HVymmhYF6eNA53PVLhnUk0znSYbH8IYBuJdCE+1luR22jNLMaQdw==
spawn-command@0.0.2:
version "0.0.2"
resolved "https://registry.yarnpkg.com/spawn-command/-/spawn-command-0.0.2.tgz#9544e1a43ca045f8531aac1a48cb29bdae62338e"
integrity sha512-zC8zGoGkmc8J9ndvml8Xksr1Amk9qBujgbF0JAIWO7kXr43w0h/0GJNM/Vustixu+YE8N/MTrQ7N31FvHUACxQ==
spdx-correct@^3.0.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.2.0.tgz#4f5ab0668f0059e34f9c00dce331784a12de4e9c"
@ -15753,7 +15781,7 @@ string-length@^4.0.1:
char-regex "^1.0.2"
strip-ansi "^6.0.0"
"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
"string-width-cjs@npm:string-width@^4.2.0":
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
@ -15771,6 +15799,15 @@ string-width@^1.0.1, string-width@^1.0.2:
is-fullwidth-code-point "^1.0.0"
strip-ansi "^3.0.0"
"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
dependencies:
emoji-regex "^8.0.0"
is-fullwidth-code-point "^3.0.0"
strip-ansi "^6.0.1"
string-width@^5.0.1, string-width@^5.1.2:
version "5.1.2"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794"
@ -15811,7 +15848,7 @@ stringify-object@^3.3.0:
is-obj "^1.0.1"
is-regexp "^1.0.0"
"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1:
"strip-ansi-cjs@npm:strip-ansi@^6.0.1":
version "6.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
@ -15832,6 +15869,13 @@ strip-ansi@^4.0.0:
dependencies:
ansi-regex "^3.0.0"
strip-ansi@^6.0.0, strip-ansi@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
dependencies:
ansi-regex "^5.0.1"
strip-ansi@^7.0.1:
version "7.1.0"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45"
@ -15941,7 +15985,7 @@ supports-color@^7.1.0:
dependencies:
has-flag "^4.0.0"
supports-color@^8.0.0:
supports-color@^8.0.0, supports-color@^8.1.1:
version "8.1.1"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c"
integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==
@ -16246,6 +16290,11 @@ tr46@~0.0.3:
resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a"
integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==
tree-kill@^1.2.2:
version "1.2.2"
resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc"
integrity sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==
trim-lines@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/trim-lines/-/trim-lines-3.0.1.tgz#d802e332a07df861c48802c04321017b1bd87338"
@ -16917,6 +16966,11 @@ vscode-textmate@^8.0.0:
resolved "https://registry.yarnpkg.com/vscode-textmate/-/vscode-textmate-8.0.0.tgz#2c7a3b1163ef0441097e0b5d6389cd5504b59e5d"
integrity sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==
vue-sfc-loader@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/vue-sfc-loader/-/vue-sfc-loader-0.1.0.tgz#630b8a47ec79e3a80bd9ba781967f6ad23bb149d"
integrity sha512-jWKKX0uGcW2nyiSv3REn4n86V4eYzPbsVWQRnxRO/bxKGrJrAMFl2aYD+38MjPsL6MblzFS/SG0jRcSkzGrElA==
vue-template-compiler@^2.7.14:
version "2.7.16"
resolved "https://registry.yarnpkg.com/vue-template-compiler/-/vue-template-compiler-2.7.16.tgz#c81b2d47753264c77ac03b9966a46637482bb03b"
@ -17267,7 +17321,7 @@ wordwrap@^1.0.0:
resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb"
integrity sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0:
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0":
version "7.0.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
@ -17293,6 +17347,15 @@ wrap-ansi@^6.0.1:
string-width "^4.1.0"
strip-ansi "^6.0.0"
wrap-ansi@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
dependencies:
ansi-styles "^4.0.0"
string-width "^4.1.0"
strip-ansi "^6.0.0"
wrap-ansi@^8.0.1, wrap-ansi@^8.1.0:
version "8.1.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"
@ -17458,7 +17521,7 @@ yargs-parser@^5.0.1:
camelcase "^3.0.0"
object.assign "^4.1.0"
yargs@17.7.2, yargs@^17.3.1, yargs@^17.6.2:
yargs@17.7.2, yargs@^17.3.1, yargs@^17.6.2, yargs@^17.7.2:
version "17.7.2"
resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269"
integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==