475 improved docs (#484)

* chore: docs
This commit is contained in:
mathuo 2024-02-25 12:32:58 +00:00 committed by GitHub
parent cc50199cff
commit e85863b65f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
237 changed files with 23018 additions and 5738 deletions

View File

@ -43,23 +43,23 @@ describe('tabsContainer', () => {
const emptySpace = cut.element
.getElementsByClassName('void-container')
.item(0);
.item(0)
if (!emptySpace) {
if (!emptySpace!) {
fail('element not found');
}
jest.spyOn(emptySpace, 'clientHeight', 'get').mockImplementation(
jest.spyOn(emptySpace!, 'clientHeight', 'get').mockImplementation(
() => 100
);
jest.spyOn(emptySpace, 'clientWidth', 'get').mockImplementation(
jest.spyOn(emptySpace!, 'clientWidth', 'get').mockImplementation(
() => 100
);
fireEvent.dragEnter(emptySpace);
fireEvent.dragOver(emptySpace);
fireEvent.dragEnter(emptySpace!);
fireEvent.dragOver(emptySpace!);
expect(groupView.canDisplayOverlay).toBeCalled();
expect(groupView.canDisplayOverlay).toHaveBeenCalled();
expect(
cut.element.getElementsByClassName('drop-target-dropzone').length
@ -102,14 +102,14 @@ describe('tabsContainer', () => {
.getElementsByClassName('void-container')
.item(0);
if (!emptySpace) {
if (!emptySpace!) {
fail('element not found');
}
jest.spyOn(emptySpace, 'clientHeight', 'get').mockImplementation(
jest.spyOn(emptySpace!, 'clientHeight', 'get').mockImplementation(
() => 100
);
jest.spyOn(emptySpace, 'clientWidth', 'get').mockImplementation(
jest.spyOn(emptySpace!, 'clientWidth', 'get').mockImplementation(
() => 100
);
@ -124,10 +124,10 @@ describe('tabsContainer', () => {
PanelTransfer.prototype
);
fireEvent.dragEnter(emptySpace);
fireEvent.dragOver(emptySpace);
fireEvent.dragEnter(emptySpace!);
fireEvent.dragOver(emptySpace!);
expect(groupView.canDisplayOverlay).toBeCalledTimes(0);
expect(groupView.canDisplayOverlay).toHaveBeenCalledTimes(0);
expect(
cut.element.getElementsByClassName('drop-target-dropzone').length
@ -173,14 +173,14 @@ describe('tabsContainer', () => {
.getElementsByClassName('void-container')
.item(0);
if (!emptySpace) {
if (!emptySpace!) {
fail('element not found');
}
jest.spyOn(emptySpace, 'clientHeight', 'get').mockImplementation(
jest.spyOn(emptySpace!, 'clientHeight', 'get').mockImplementation(
() => 100
);
jest.spyOn(emptySpace, 'clientWidth', 'get').mockImplementation(
jest.spyOn(emptySpace!, 'clientWidth', 'get').mockImplementation(
() => 100
);
@ -189,10 +189,10 @@ describe('tabsContainer', () => {
PanelTransfer.prototype
);
fireEvent.dragEnter(emptySpace);
fireEvent.dragOver(emptySpace);
fireEvent.dragEnter(emptySpace!);
fireEvent.dragOver(emptySpace!);
expect(groupView.canDisplayOverlay).toBeCalledTimes(0);
expect(groupView.canDisplayOverlay).toHaveBeenCalledTimes(0);
expect(
cut.element.getElementsByClassName('drop-target-dropzone').length
@ -238,14 +238,14 @@ describe('tabsContainer', () => {
.getElementsByClassName('void-container')
.item(0);
if (!emptySpace) {
if (!emptySpace!) {
fail('element not found');
}
jest.spyOn(emptySpace, 'clientHeight', 'get').mockImplementation(
jest.spyOn(emptySpace!, 'clientHeight', 'get').mockImplementation(
() => 100
);
jest.spyOn(emptySpace, 'clientWidth', 'get').mockImplementation(
jest.spyOn(emptySpace!, 'clientWidth', 'get').mockImplementation(
() => 100
);
@ -254,10 +254,10 @@ describe('tabsContainer', () => {
PanelTransfer.prototype
);
fireEvent.dragEnter(emptySpace);
fireEvent.dragOver(emptySpace);
fireEvent.dragEnter(emptySpace!);
fireEvent.dragOver(emptySpace!);
expect(groupView.canDisplayOverlay).toBeCalledTimes(0);
expect(groupView.canDisplayOverlay).toHaveBeenCalledTimes(0);
expect(
cut.element.getElementsByClassName('drop-target-dropzone').length
@ -302,14 +302,14 @@ describe('tabsContainer', () => {
.getElementsByClassName('void-container')
.item(0);
if (!emptySpace) {
if (!emptySpace!) {
fail('element not found');
}
jest.spyOn(emptySpace, 'clientHeight', 'get').mockImplementation(
jest.spyOn(emptySpace!, 'clientHeight', 'get').mockImplementation(
() => 100
);
jest.spyOn(emptySpace, 'clientWidth', 'get').mockImplementation(
jest.spyOn(emptySpace!, 'clientWidth', 'get').mockImplementation(
() => 100
);
@ -324,10 +324,10 @@ describe('tabsContainer', () => {
PanelTransfer.prototype
);
fireEvent.dragEnter(emptySpace);
fireEvent.dragOver(emptySpace);
fireEvent.dragEnter(emptySpace!);
fireEvent.dragOver(emptySpace!);
expect(groupView.canDisplayOverlay).toBeCalledTimes(1);
expect(groupView.canDisplayOverlay).toHaveBeenCalledTimes(1);
expect(
cut.element.getElementsByClassName('drop-target-dropzone').length
@ -507,7 +507,7 @@ describe('tabsContainer', () => {
const eventPreventDefaultSpy = jest.spyOn(event, 'preventDefault');
fireEvent(container, event);
expect(accessor.addFloatingGroup).toBeCalledWith(
expect(accessor.addFloatingGroup).toHaveBeenCalledWith(
groupPanel,
{
x: 100,
@ -567,15 +567,15 @@ describe('tabsContainer', () => {
const eventPreventDefaultSpy = jest.spyOn(event, 'preventDefault');
fireEvent(container, event);
expect(accessor.addFloatingGroup).toBeCalledTimes(0);
expect(eventPreventDefaultSpy).toBeCalledTimes(0);
expect(accessor.addFloatingGroup).toHaveBeenCalledTimes(0);
expect(eventPreventDefaultSpy).toHaveBeenCalledTimes(0);
const event2 = new KeyboardEvent('mousedown', { shiftKey: false });
const eventPreventDefaultSpy2 = jest.spyOn(event2, 'preventDefault');
fireEvent(container, event2);
expect(accessor.addFloatingGroup).toBeCalledTimes(0);
expect(eventPreventDefaultSpy2).toBeCalledTimes(0);
expect(accessor.addFloatingGroup).toHaveBeenCalledTimes(0);
expect(eventPreventDefaultSpy2).toHaveBeenCalledTimes(0);
});
test('that selecting a tab with shift down will move that tab into a new floating group', () => {

View File

@ -170,10 +170,10 @@ type AddPanelPositionUnion = {
type AddPanelOptionsUnion = AddPanelFloatingGroupUnion | AddPanelPositionUnion;
export type AddPanelOptions<P extends object = Parameters> = Omit<
PanelOptions<P>,
'component' | 'tabComponent'
> & {
export type AddPanelOptions<P extends object = Parameters> = {
params?: P;
id: string;
title?: string;
component: string;
tabComponent?: string;
renderer?: DockviewPanelRenderer;

View File

@ -0,0 +1,41 @@
---
slug: dockview-1.10.0-release
title: Dockview 1.10.0
tags: [release]
---
# Release Notes
Please reference to docs @ [dockview.dev](https://dockview.dev).
## 🚀 Features
- New Documentation Website [#475](https://github.com/mathuo/dockview/issues/475)
- Documentation website improved to cater for growing libary
- Panel Gaps [#447](https://github.com/mathuo/dockview/issues/447)
- CSS Variable `--dv-group-gap-size` exposed.
- Locked Dock [#460](https://github.com/mathuo/dockview/issues/460)
- `locked` option prevents all user resizing of dock.
- Enhance Window Popout [#469](https://github.com/mathuo/dockview/issues/469)
- Numerous improved to Window popouts including replacement of window to original location
when popout is closed.
- Enhance Events [#479](https://github.com/mathuo/dockview/issues/479)
- Drag and Drop Events [#395](https://github.com/mathuo/dockview/issues/395)
## 🛠 Miscs
- Bug: Drop Target Scaling [#448](https://github.com/mathuo/dockview/issues/448)
- Drop target overlays are no longer scaled with `transform` which allows for better CSS customization.
- Bug: Popout `always` rendeing mode [#486](https://github.com/mathuo/dockview/issues/486)
- `always` render mode now works in Popout Windows too.
- Bug: React Component Support [#498](https://github.com/mathuo/dockview/issues/498)
- Support components wrapped with `React.memo(...)` passed in `components` option.
- Bug: Remove Source Maps [#509](https://github.com/mathuo/dockview/issues/509)
- Source maps do not currently work. They have been removed and will be re-evaluated for a later release.
## 🔥 Breaking changes
- Bug: Typo [#495](https://github.com/mathuo/dockview/issues/495)
- Rename `onDidMaxmizedNodeChange` to `onDidMaximizedNodeChange`
- Rename `exitMaxmizedGroup` to `exitMaximizedGroup`
- Rename `onDidMaxmizedGroupChange` to `onDidMaximizedGroupChange`

View File

@ -0,0 +1,5 @@
{
"label": "Advanced",
"collapsible": false,
"collapsed": false
}

View File

@ -0,0 +1,8 @@
import { MultiFrameworkContainer } from '@site/src/components/ui/container';
import DockviewNative2 from '@site/sandboxes/nativeapp-dockview/src/app';
# Window-like mananger with tabs
<MultiFrameworkContainer sandboxId="nativeapp-dockview" react={DockviewNative2} />

View File

@ -0,0 +1,12 @@
import { MultiFrameworkContainer } from '@site/src/components/ui/container';
import EventsDockview from '@site/sandboxes/events-dockview/src/app';
# Events
A simple example showing events fired by `dockviewz that can be interacted with.
<MultiFrameworkContainer
height={600}
sandboxId="events-dockview"
react={EventsDockview}
/>

View File

@ -0,0 +1,30 @@
import { MultiFrameworkContainer } from '@site/src/components/ui/container';
import DockviewWithIFrames from '@site/sandboxes/iframe-dockview/src/app';
# iframes
iframes reload when repositioned within the DOM which can cause issues.
iFrames required special attention because of a particular behaviour in how iFrames render:
> Re-parenting an iFrame will reload the contents of the iFrame or the rephrase this, moving an iFrame within the DOM will cause a reload of its contents.
You can find many examples of discussions on this. Two reputable forums for example are linked [here](https://bugzilla.mozilla.org/show_bug.cgi?id=254144) and [here](https://github.com/whatwg/html/issues/5484).
To ensure iFrames work as expected you should render them in panels with `renderer: 'always'` to ensure they are never removed from the DOM, alternatively set the defaultRenderer to `always`.
> See the [Panel Rendering](/core/panels/rendering.mdx) section for more information of render modes.
```tsx title="Example of a panel using an alternative renderer"
api.addPanel({
id: 'my_panel_id',
component: 'my_component',
renderer: 'always',
});
```
<MultiFrameworkContainer
sandboxId="iframe-dockview"
height={600}
react={DockviewWithIFrames}
/>

View File

@ -0,0 +1,16 @@
---
title: Keyboard
---
import { MultiFrameworkContainer } from '@site/src/components/ui/container';
import DockviewKeyboard from '@site/sandboxes/keyboard-dockview/src/app';
# Keyboard Navigation
Keyboard shortcuts
<MultiFrameworkContainer
height={600}
sandboxId="keyboard-dockview"
react={DockviewKeyboard}
/>

View File

@ -0,0 +1,14 @@
---
title: Nested Instances
---
import { MultiFrameworkContainer } from '@site/src/components/ui/container';
import NestedDockview from '@site/sandboxes/nested-dockview/src/app';
# 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} />

View File

@ -0,0 +1,14 @@
---
title: Group API
sidebar_position: 3
---
:::info
Use the group API sparingly. As you move panels, groups change and if you don't track this correctly you may encounter unexpected
behaviours. You should be able to achieve most things directly through the panel API.
:::
import { DocRef } from '@site/src/components/ui/reference/docRef';
<DocRef declaration="DockviewGroupPanelApi" />

View File

@ -0,0 +1,18 @@
---
title: Options
sidebar_position: 0
---
import { DocRef } from '@site/src/components/ui/reference/docRef';
<FrameworkSpecific framework="JavaScript">
<DocRef declaration="DockviewComponentOptions" />
</FrameworkSpecific>
<FrameworkSpecific framework="React">
<DocRef declaration="IDockviewReactProps" />
</FrameworkSpecific>

View File

@ -0,0 +1,10 @@
---
title: API
sidebar_position: 1
---
import { DocRef } from '@site/src/components/ui/reference/docRef';
This section describes the api object.
<DocRef declaration="DockviewApi" />

View File

@ -0,0 +1,10 @@
---
description: API
title: Panel API
sidebar_position: 2
---
import { DocRef } from '@site/src/components/ui/reference/docRef';
<DocRef declaration="DockviewPanelApi" />

View File

@ -0,0 +1,9 @@
---
description: API
title: "API"
sidebar_position: 1
---
import { DocRef } from '@site/src/components/ui/reference/docRef';
<DocRef declaration="GridviewApi" />

View File

@ -0,0 +1,18 @@
---
title: Options
sidebar_position: 0
---
import { DocRef } from '@site/src/components/ui/reference/docRef';
<FrameworkSpecific framework="JavaScript">
<DocRef declaration="GridviewComponentOptions" />
</FrameworkSpecific>
<FrameworkSpecific framework="React">
<DocRef declaration="IGridviewReactProps" />
</FrameworkSpecific>

View File

@ -0,0 +1,10 @@
---
description: API
title: Panel API
sidebar_position: 2
---
import { DocRef } from '@site/src/components/ui/reference/docRef';
<DocRef declaration="GridviewPanelApi" />

View File

@ -0,0 +1,9 @@
---
description: API
title: "API"
sidebar_position: 1
---
import { DocRef } from '@site/src/components/ui/reference/docRef';
<DocRef declaration="PaneviewApi" />

View File

@ -0,0 +1,18 @@
---
title: Options
sidebar_position: 0
---
import { DocRef } from '@site/src/components/ui/reference/docRef';
<FrameworkSpecific framework="JavaScript">
<DocRef declaration="PaneviewComponentOptions" />
</FrameworkSpecific>
<FrameworkSpecific framework="React">
<DocRef declaration="IPaneviewReactProps" />
</FrameworkSpecific>

View File

@ -0,0 +1,10 @@
---
description: API
title: Panel API
sidebar_position: 2
---
import { DocRef } from '@site/src/components/ui/reference/docRef';
<DocRef declaration="PaneviewPanelApi" />

View File

@ -0,0 +1,9 @@
---
description: API
title: "API"
sidebar_position: 1
---
import { DocRef } from '@site/src/components/ui/reference/docRef';
<DocRef declaration="SplitviewApi" />

View File

@ -0,0 +1,18 @@
---
title: Options
sidebar_position: 0
---
import { DocRef } from '@site/src/components/ui/reference/docRef';
<FrameworkSpecific framework="JavaScript">
<DocRef declaration="SplitviewComponentOptions" />
</FrameworkSpecific>
<FrameworkSpecific framework="React">
<DocRef declaration="ISplitviewReactProps" />
</FrameworkSpecific>

View File

@ -0,0 +1,10 @@
---
description: API
title: Panel API
sidebar_position: 2
---
import { DocRef } from '@site/src/components/ui/reference/docRef';
<DocRef declaration="SplitviewPanelApi" />

View File

@ -1,10 +1,6 @@
{
"label": "Components",
"position": 5,
"label": "Tabs",
"collapsible": true,
"collapsed": false,
"position": 2,
"link": {
"type": "generated-index",
"title": "Components"
}
"collapsed": false
}

View File

@ -7,15 +7,15 @@ import { MultiFrameworkContainer } from '@site/src/components/ui/container';
import Link from '@docusaurus/Link';
import useBaseUrl from '@docusaurus/useBaseUrl';
import DockviewPersistence from '@site/sandboxes/layout-dockview/src/app';
// import DockviewPersistence from '@site/sandboxes/layout-dockview/src/app';
import SimpleDockview from '@site/sandboxes/simple-dockview/src/app';
import ResizeDockview from '@site/sandboxes/resize-dockview/src/app';
import DockviewWatermark from '@site/sandboxes/watermark-dockview/src/app';
import DockviewConstraints from '@site/sandboxes/constraints-dockview/src/app';
// import DockviewWatermark from '@site/sandboxes/watermark-dockview/src/app';
// import DockviewConstraints from '@site/sandboxes/constraints-dockview/src/app';
import DndDockview from '@site/sandboxes/dnd-dockview/src/app';
import NestedDockview from '@site/sandboxes/nested-dockview/src/app';
import EventsDockview from '@site/sandboxes/events-dockview/src/app';
import DockviewGroupControl from '@site/sandboxes/headeractions-dockview/src/app';
// import DockviewGroupControl from '@site/sandboxes/headeractions-dockview/src/app';
import CustomHeadersDockview from '@site/sandboxes/customheader-dockview/src/app';
import DockviewNative from '@site/sandboxes/fullwidthtab-dockview/src/app';
import DockviewNative2 from '@site/sandboxes/nativeapp-dockview/src/app';
@ -25,13 +25,13 @@ import DockviewExternalDnd from '@site/sandboxes/externaldnd-dockview/src/app';
import DockviewResizeContainer from '@site/sandboxes/resizecontainer-dockview/src/app';
import DockviewTabheight from '@site/sandboxes/tabheight-dockview/src/app';
import DockviewWithIFrames from '@site/sandboxes/iframe-dockview/src/app';
import DockviewFloating from '@site/sandboxes/floatinggroup-dockview/src/app';
// import DockviewFloating from '@site/sandboxes/floatinggroup-dockview/src/app';
import DockviewLockedGroup from '@site/sandboxes/lockedgroup-dockview/src/app';
import DockviewKeyboard from '@site/sandboxes/keyboard-dockview/src/app';
import DockviewPopoutGroup from '@site/sandboxes/popoutgroup-dockview/src/app';
import DockviewMaximizeGroup from '@site/sandboxes/maximizegroup-dockview/src/app';
import DockviewRenderMode from '@site/sandboxes/rendermode-dockview/src/app';
import DockviewScrollbars from '@site/sandboxes/scrollbars-dockview/src/app';
// import DockviewPopoutGroup from '@site/sandboxes/popoutgroup-dockview/src/app';
// import DockviewMaximizeGroup from '@site/sandboxes/maximizegroup-dockview/src/app';
// import DockviewRenderMode from '@site/sandboxes/rendermode-dockview/src/app';
// import DockviewScrollbars from '@site/sandboxes/scrollbars-dockview/src/app';
import DockviewFocus from '@site/sandboxes/focus-dockview/src/app';
@ -199,10 +199,10 @@ const onReady = (event: DockviewReadyEvent) => {
Here is an example using the above code loading from and saving to localStorage.
If you refresh the page you should notice your layout is loaded as you left it.
<MultiFrameworkContainer
{/* <MultiFrameworkContainer
sandboxId="layout-dockview"
react={DockviewPersistence}
/>
/> */}
## Scrollbars
@ -215,10 +215,10 @@ The following container three views:
- **Panel 3**: Sets `height: 100%` and defines an inner component with `overflow: auto` to enable the scrollbars.
<MultiFrameworkContainer
{/* <MultiFrameworkContainer
sandboxId="scrollbars-dockview"
react={DockviewScrollbars}
/>
/> */}
## Resizing
@ -264,10 +264,10 @@ When the dockview is empty you may want to display some fallback content, this i
By default there the watermark has no content but you can provide as a prop to `DockviewReact` a `watermarkComponent`
which will be rendered when there are no panels or groups.
<MultiFrameworkContainer
{/* <MultiFrameworkContainer
sandboxId="watermark-dockview"
react={DockviewWatermark}
/>
/> */}
## Drag And Drop
@ -402,11 +402,11 @@ You can control the bounding box of floating groups through the optional `floati
- `{minimumHeightWithinViewport?: number, minimumWidthWithinViewport?: number}` sets the respective dimension minimums that must appears within the docks viewport
- If no options are provided the defaults of `100px` minimum height and width within the viewport are set.
<MultiFrameworkContainer
{/* <MultiFrameworkContainer
height={600}
sandboxId="floatinggroup-dockview"
react={DockviewFloating}
/>
/> */}
## Popout Groups
@ -447,11 +447,11 @@ const group = props.containerApi.addGroup();
props.group.api.moveTo({ group });
```
<MultiFrameworkContainer
{/* <MultiFrameworkContainer
height={600}
sandboxId="popoutgroup-dockview"
react={DockviewPopoutGroup}
/>
/> */}
## Maximized Groups
@ -486,11 +486,11 @@ const result: boolean = api.isMaxmized();
api.exitMaximized();
```
<MultiFrameworkContainer
{/* <MultiFrameworkContainer
height={600}
sandboxId="maximizegroup-dockview"
react={DockviewMaximizeGroup}
/>
/> */}
## Panels
@ -663,11 +663,11 @@ This design allows for maximum performance at some cost.
- `always` mode. In this mode when panels become hidden the HTMLElement is not destroyed so all DOM state such as scrollbar positions will be maintained. This is implemented by rendering each panel as an absolutely positioned
HTMLElement and hidden the HTMLElement with `display: none` when it should be hidden.
<MultiFrameworkContainer
{/* <MultiFrameworkContainer
height={500}
sandboxId="rendermode-dockview"
react={DockviewRenderMode}
/>
/> */}
####
@ -937,10 +937,10 @@ const RightHeaderActionsComponent = (props: IDockviewHeaderActionsProps) => {
};
```
<MultiFrameworkContainer
{/* <MultiFrameworkContainer
sandboxId="groupcontrol-dockview"
react={DockviewGroupControl}
/>
/> */}
### Constraints
@ -954,11 +954,11 @@ api.group.api.setConstraints(...)
> If you specific a constraint on a group and move a panel within that group to another group it will no
> longer be subject to those constraints since those constraints were on the group and not on the individual panel.
<MultiFrameworkContainer
{/* <MultiFrameworkContainer
height={500}
sandboxId="constraints-dockview"
react={DockviewConstraints}
/>
/> */}
## iFrames

View File

@ -0,0 +1,78 @@
---
title: Get Started
sidebar_position: 0
---
import { MultiFrameworkContainer } from '@site/src/components/ui/container';
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
sandboxId="example-app-dockview"
react={DockviewExampleApp}
/>
## Introduction
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}
/>
<br />
> You can access the panels associated group through the `panel.group` variable.
> The group will always be defined and will change if a panel is moved into another group.
## Dockview API
The Dockview API is exposed both at the `onReady` event and on each panel through `props.containerApi`.
Through this API you can control general features of the component and access all added panels.
```tsx title="Dockview API via Panel component"
const MyComponent = (props: IDockviewPanelProps<{ title: string }>) => {
// props.containerApi...
return <div>{`My first panel has the title: ${props.params.title}`}</div>;
};
```
```tsx title="Dockview API via the onReady callback"
const onReady = (event: DockviewReadyEvent) => {
// event.api...
};
```
## Dockview Panel API
```tsx
const MyComponent = (props: IDockviewPanelProps<{ title: string }>) => {
// props.api...
return <div>{`My first panel has the title: ${props.params.title}`}</div>;
};
```

View File

@ -0,0 +1,6 @@
{
"position": 3,
"label": "Drag & Drop",
"collapsible": true,
"collapsed": true
}

View File

@ -0,0 +1,20 @@
---
title: 'Disable Dnd'
sidebar_position: 3
---
import { DocRef } from '@site/src/components/ui/reference/docRef';
<FrameworkSpecific framework="JavaScript">
<DocRef declaration="DockviewComponentOptions" methods={["disableDnd"]} />
</FrameworkSpecific>
<FrameworkSpecific framework="React">
<DocRef declaration="IDockviewReactProps" methods={["disableDnd"]} />
</FrameworkSpecific>

View File

@ -0,0 +1,97 @@
---
title: 'Dnd'
sidebar_position: 1
---
import useBaseUrl from '@docusaurus/useBaseUrl';
import { MultiFrameworkContainer } from '@site/src/components/ui/container';
import DndDockview from '@site/sandboxes/dnd-dockview/src/app';
import DockviewExternalDnd from '@site/sandboxes/externaldnd-dockview/src/app';
import LiveExample from '@site/src/components/ui/exampleFrame';
import { DocRef } from '@site/src/components/ui/reference/docRef';
The dock makes heavy use of drag and drop functionalities.
<DocRef declaration="DockviewApi"
methods={[
'onWillDragPanel', 'onWillDragGroup',
'onWillDrop', 'onDidDrop', 'onWillShowOverlay'
]}
/>
<LiveExample framework='react' id='dockview/dnd-events' />
# Drag And Drop
You can override the conditions of the far edge overlays through the `rootOverlayModel` prop.
```tsx
<DockviewReact
{...props}
rootOverlayModel={{
size: { value: 100, type: 'pixels' },
activationSize: { value: 5, type: 'percentage' },
}}
/>
```
## Extended behaviours
For interaction with the Drag events directly the component exposes some method to help determine whether external drag events should be interacted with or not.
```tsx
/**
* called when an ondrop event which does not originate from the dockview libray and
* passes the showDndOverlay condition occurs
**/
const onDidDrop = (event: DockviewDropEvent) => {
const { group } = event;
event.api.addPanel({
id: 'test',
component: 'default',
position: {
referencePanel: group.activePanel.id,
direction: 'within',
},
});
};
/**
* called for drag over events which do not originate from the dockview library
* allowing the developer to decide where the overlay should be shown for a
* particular drag event
**/
const showDndOverlay = (event: DockviewDndOverlayEvent) => {
return true;
};
return (
<DockviewReact
components={components}
onReady={onReady}
className="dockview-theme-abyss"
onDidDrop={onDidDrop}
showDndOverlay={showDndOverlay}
/>
);
```
## Intercepting Drag Events
You can intercept drag events to attach your own metadata using the `onWillDragPanel` and `onWillDragGroup` api methods.
<MultiFrameworkContainer sandboxId="dnd-dockview" react={DndDockview} />
## Third Party Dnd Libraries
This shows a simple example of a third-party library used inside a panel that relies on drag
and drop functionalities. This examples serves to show that `dockview` doesn't interfer with
any drag and drop logic for other controls.
<MultiFrameworkContainer
sandboxId="externaldnd-dockview"
react={DockviewExternalDnd}
/>

View File

@ -0,0 +1,44 @@
---
title: 'Overview'
sidebar_position: 0
---
import useBaseUrl from '@docusaurus/useBaseUrl';
import { MultiFrameworkContainer } from '@site/src/components/ui/container';
import DndDockview from '@site/sandboxes/dnd-dockview/src/app';
import DockviewExternalDnd from '@site/sandboxes/externaldnd-dockview/src/app';
import { DocRef } from '@site/src/components/ui/reference/docRef';
Dockview supports a wide variety of built-in Drag and Drop possibilities.
<h4>Position a tab between two other tabs</h4>
<div style={{display:' flex', justifyContent: 'center'}}>
<img style={{ height: '50px' }} src={useBaseUrl('/img/add_to_tab.svg')} />
</div>
<h4>Position a tab at the end of a list of tabs</h4>
<div style={{display:' flex', justifyContent: 'center'}}>
<img style={{ height: '50px' }} src={useBaseUrl('/img/add_to_empty_space.svg')} />
</div>
<h4>Merge one group with another group</h4>
<div style={{display:' flex', justifyContent: 'center'}}>
<img style={{ height: '50px' }} src={useBaseUrl('/img/add_to_group.svg')} />
</div>
<h4>Move both Tabs and Groups in relation to another group</h4>
<div style={{display:' flex', justifyContent: 'center'}}>
<img style={{ height: '300px' }} src={useBaseUrl('/img/drop_positions.svg')} />
</div>
<h4>Move both Tabs and Groups in relation to the container</h4>
<div style={{display:' flex', justifyContent: 'center'}}>
<img style={{ height: '300px' }} src={useBaseUrl('/img/magnet_drop_positions.svg')} />
</div>

View File

@ -0,0 +1,13 @@
---
title: 'Third Party Libraries'
sidebar_position: 2
---
All third party Drag & Drop libraries should work as expected.
Dockview fire and intercepts Drag & Drop events extensively however it is indended that the user has ultimate
control over all events.
Dockview should not change the behaviours of any third party Drag & Drop libraries.
If you feel that Dockview is cause the behaviour of any third party Drag & Drop libraries to change please
raise an Issue.

View File

@ -0,0 +1,6 @@
{
"position": 3,
"label": "Groups",
"collapsible": true,
"collapsed": true
}

View File

@ -0,0 +1,17 @@
---
title: Constraints
---
import LiveExample from '@site/src/components/ui/exampleFrame';
import { DocRef } from '@site/src/components/ui/reference/docRef'
:::warning
Constraints come with several caveats. They are not serialized with layouts and can only be applied to groups.
:::
<DocRef declaration="DockviewGroupPanelApi" methods={['setConstraints', 'onDidConstraintsChange']} />
## Live Example
<LiveExample framework="react" id="dockview/constraints"/>

View File

@ -0,0 +1,43 @@
---
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.
<FrameworkSpecific framework='React'>
<DocRef declaration="IDockviewReactProps" methods={['leftHeaderActionsComponent', 'rightHeaderActionsComponent', 'prefixHeaderActionsComponent']} />
</FrameworkSpecific>
<FrameworkSpecific framework='JavaScript'>
<DocRef declaration="DockviewComponentOptions"
methods={['createLeftHeaderActionsElement', 'createRightHeaderActionsElement', 'createPrefixHeaderActionsElement']} />
</FrameworkSpecific>
```tsx
const LeftComponent = (props: IDockviewHeaderActionsProps) => {
return <div>{/** content */}</div>;
};
const RightComponent = (props: IDockviewHeaderActionsProps) => {
return <div>{/** content */}</div>;
};
const PrefixComponent = (props: IDockviewHeaderActionsProps) => {
return <div>{/** content */}</div>;
};
return <DockviewReact
leftHeaderActionsComponent={LeftComponent}
rightHeaderActionsComponent={RightComponent}
prefixHeaderActionsComponent={PrefixComponent}
/>;
```
## Live Example
<LiveExample framework="react" id="dockview/group-actions"/>

View File

@ -0,0 +1,79 @@
---
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.
:::info
Floating groups **cannot** be maximized. Calling maximize function on groups in these states will have no effect.
:::
Dockview has built-in support for floating groups. Each floating container can contain a single group with many panels
and you can have as many floating containers as needed. You cannot dock multiple groups together in the same floating container.
## Options
The following properties can be set to configure the behaviours of floating groups.
<FrameworkSpecific framework='React'>
<DocRef declaration="IDockviewReactProps" methods={['floatingGroupBounds', 'disableFloatingGroups']} />
</FrameworkSpecific>
<FrameworkSpecific framework='JavaScript'>
<DocRef declaration="DockviewComponentOptions" methods={['floatingGroupBounds', 'disableFloatingGroups']} />
</FrameworkSpecific>
You can control the bounding box of floating groups through the optional `floatingGroupBounds` options:
- `boundedWithinViewport` will force the entire floating group to be bounded within the docks viewport.
- `{minimumHeightWithinViewport?: number, minimumWidthWithinViewport?: number}` sets the respective dimension minimums that must appears within the docks viewport
- If no options are provided the defaults of `100px` minimum height and width within the viewport are set.
## API
The following properties can be used to create floating groups
<DocRef declaration="DockviewApi" methods={['addFloatingGroup']} />
:::info
`addFloatingGroup` only accepts existing panels and groups. See [Addding Panels](/docs/core/panels/add) on how to firstly add panels.
:::
Floating groups can be programatically added through the dockview `api` method `api.addFloatingGroup(...)`.
## Panel and Group API
<DocRef declaration="DockviewPanelApi" methods={['location', 'onDidLocationChange']} />
You can check whether a group is floating via the `group.api.location` property. See examples for full code.
## Working with Floating Groups
<h4>Float an existing tab by holding `shift` whilst interacting with the tab</h4>
<div style={{display:' flex', justifyContent: 'center'}}>
<img style={{ height: '200px' }} src={useBaseUrl('/img/float_add.svg')} />
</div>
<h4>Move a floating tab by holding `shift` whilst moving the cursor or dragging the empty header space</h4>
<div style={{display:' flex', justifyContent: 'center'}}>
<img style={{ height: '200px' }} src={useBaseUrl('/img/float_move.svg')} />
</div>
<h4>Move an entire floating group by holding `shift` whilst dragging the empty header space</h4>
<div style={{display:' flex', justifyContent: 'center'}}>
<img style={{ height: '200px' }} src={useBaseUrl('/img/float_group.svg')} />
</div>
## Live Example
<LiveExample framework="react" id="dockview/floating-groups"/>

View File

@ -0,0 +1,13 @@
---
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`.
```tsx
panel.group.header.hidden = true;
```

View File

@ -0,0 +1,27 @@
---
title: Locked Groups
---
import { MultiFrameworkContainer } from '@site/src/components/ui/container';
import DockviewLockedGroup from '@site/sandboxes/lockedgroup-dockview/src/app';
## Locked group
Locking a group will disable all drop events for this group ensuring no additional panels can be added to the group through drop events.
You can still add groups to a locked panel programatically using the API though.
```tsx
panel.group.locked = true;
// Or
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}
/>

View File

@ -0,0 +1,58 @@
---
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.
## Options
<DocRef declaration="DockviewApi" methods={['maximizeGroup', 'hasMaximizedGroup', 'exitMaximizedGroup', 'onDidMaxmizedGroupChange']} />
```tsx
const api: DockviewApi;
// maximize a specified group
api.maxmimizeGroup(group);
// check whether a specific group is maximized
const result: boolean = api.isMaximizedGroup(group);
// if there is any maximized group exit the maximized state
exitMaximizedGroup();
// is there a maximized group
const result: boolean = hasMaximizedGroup();
```
## Panel API
<DocRef declaration="DockviewPanelApi" methods={['maximize', 'isMaximized', 'exitMaximized']} />
```tsx
const api: DockviewPanelApi;
// maximize the group
api.maximize();
// is this group maximized (if another group is maximized this method will still return false)
const result: boolean = api.isMaxmized();
// exit only if this group is maximzied (if another group is maxmized this has no affect)
api.exitMaximized();
```
:::tip
`api.<maximize|isMaximized|exitMaximized>` is equivalent to `api.group.api.<maximize|isMaximized|exitMaximized>`.
The methods exist on the panel `api` object for convenience.
:::
## Live Examples
<LiveExample framework="react" id="dockview/maximize-group"/>

View File

@ -0,0 +1,21 @@
---
title: Move Group
sidebar_position: 5
---
import { DocRef } from '@site/src/components/ui/reference/docRef';
This section describes how you can move a group.
## Methods
<DocRef declaration="DockviewGroupPanelApi" methods={["moveTo"]}/>
## Move a Group
You can move a group through the [Group API](/docs/api/dockview/groupApi) and you can find out how to move a Panel [here](/docs/core/panels/move).
```ts
panel.group.api.moveTo({ group, position, index });
```

View File

@ -0,0 +1,60 @@
---
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.
:::info
Popout groups **cannot** be maximized. Calling maximize function on groups in these states will have no effect.
:::
<DocRef declaration="DockviewApi" methods={['addPopoutGroup']} />
Dockview has built-in support for opening groups in new Windows.
Each popout window can contain a single group with many panels and you can have as many popout
windows as needed. You cannot dock multiple groups together in the same window.
Popout windows require your website to have a blank `.html` page that can be used, by default this is set to `/popout.html` but
can be configured to match requirements.
```tsx
api.addPopoutGroup(
group,
// the second arguments (options) is optional
{
popoutUrl:"/popout.html",
box: { left: 0, top: 0, height: 200, width: 300 }
});
```
> If you do not provide `options.popoutUrl` a default of `/popout.html` is used and if `options.box` is not provided
the view will be places according to it's currently position.
From within a panel you may say
```tsx
props.containerApi.addPopoutGroup(props.api.group);
```
## Closing the Popout Group
To programatically move the popout group back into the main grid you can use the `moveTo` method in many ways, one of the following would suffice
```tsx
// option 1: add absolutely to the right-side of the grid
props.group.api.moveTo({ position: 'right' });
// option 2: create a new group and move the contents of the popout group to it
const group = props.containerApi.addGroup();
props.group.api.moveTo({ group });
```
Alternatively, if the user closes the Window the group the dock will make a best attempt to place it back
in it's original location within the grid. If the dock cannot determine the original location it will
choose a new location.
<LiveExample framework="react" id="dockview/popout-group"/>

View File

@ -0,0 +1,37 @@
---
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']} />
## Panel Resizing
Each Dockview contains of a number of groups and each group has a number of panels.
Logically a user may want to resize a panel, but this translates to resizing the group which contains that panel.
You can set the size of a panel using `props.api.setSize(...)`.
You can also set the size of the group associated with the panel using `props.api.group.api.setSize(...)` although this isn't recommended
due to the clunky syntax.
```tsx
// it's mandatory to provide either a height or a width, providing both is optional
props.api.setSize({
height: 100,
width: 200,
});
// you could also resize the panels group, although not recommended it achieved the same result
props.api.group.api.setSize({
height: 100,
width: 200,
});
```
You can see an example invoking both approaches below.
<LiveExample framework="react" id="dockview/resize"/>

View File

@ -0,0 +1,12 @@
---
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.
<LiveExample framework='react' id='dockview/locked'/>

View File

@ -0,0 +1,42 @@
---
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
once you have created a dock you can store a reference to the [API](/docs/api/dockview/overview) that is created.
<FrameworkSpecific framework='React'>
```tsx
function onReady(event: DockviewReadyEvent) {
/**
* You should store a reference to `api` in a Ref or State
* for later interactions
*/
const api: DockviewApi = event.api;
}
<DockviewReact onReady={onReady}/>
```
</FrameworkSpecific>
<FrameworkSpecific framework='JavaScript'>
```tsx
const component = new DockviewComponent({
/** options */
});
```
</FrameworkSpecific>
## Container Resizing
The component will automatically resize to it's container.
<LiveExample framework="react" id="dockview/resize-container"/>
# Disposal Pattern

View File

@ -0,0 +1,6 @@
{
"position": 1,
"label": "Panels",
"collapsible": true,
"collapsed": true
}

View File

@ -0,0 +1,167 @@
---
title: Adding Panels
sidebar_position: 1
---
import { DocRef } from '@site/src/components/ui/reference/docRef';
import LiveExample from '@site/src/components/ui/exampleFrame';
This section describes how to add a new panel and the options you can provide.
Panels can be added through the dock api.
<DocRef declaration="DockviewApi" methods={['addPanel']} />
## Opening a Basic Panel
To open a panel requires a unique `id` and the name of the `component` to render.
```ts
const panel: IDockviewPanel = api.addPanel({
id: 'my_unique_panel_id',
component: 'my_component',
});
```
> See [Overview](/docs/core/overview) to register components.
## Providing a Panel Title
:::warning
Registering and updating the title using these built-in variables only works for the default tab renderer.
If you use a custom tab render you can optionally access these variables to render the title, or you can take
your own approach to rendering a tab title.
:::
Use `title` to provide a custom title for the panel. If no `title` is provided then the dock will render `id` in the tab.
```tsx
api.addPanel({
id: 'panel_1',
component: 'my_component',
title: 'my_custom_title',
});
```
```tsx
api.setTitle('my_new_custom_title');
```
<LiveExample framework="react" id="dockview/update-title" height={250}/>
## Provide a custom Tab renderer
:::info
You can override the default tab renderer through the [Options](/docs/api/dockview/options).
:::
To render a custom tab component you should specify the `tabComponent`.
```ts
const panel: IDockviewPanel = api.addPanel({
id: 'my_unique_panel_id',
component: 'my_component',
tabComponent: 'my_tab_component',
});
```
> See [Tabs](/docs/core/panels/tabs) to learn how to register tab components.
## Provide custom Parameters
Using the `params` option you can specific a simple object that is accessible in both the panel and tab renderer.
To update these parameters after the panel has been created see [Update Panel](/docs/core/panels/update).
```ts
const panel: IDockviewPanel = api.addPanel({
id: 'my_unique_panel_id',
component: 'my_component',
params: {
myCustomKey: 'my_custom_value',
},
});
```
## Rendering
See [Panel Rendering](/docs/core/panels/rendering).
## Positioning the Panel
You can position a panel relative to an existing panel, group using `direction`. If you do not provide a reference panel
or group then the panel will be positioned to the edge of the dock in the specified direction.
#### Relative to another Panel
```ts
const panel2: IDockviewPanel = api.addPanel({
id: 'panel_2',
component: 'default',
position: {
referencePanel: 'panel_1',
direction: 'above'
}
});
api.addPanel({
id: 'panel_3',
component: 'default',
position: {
referencePanel: panel2,
direction: 'above'
}
});
```
#### Relative to another Group
```ts
const panel2: IDockviewPanel = api.addPanel({
id: 'panel_2',
component: 'default',
position: {
referenceGroup: 'panel_1',
direction: 'left'
}
});
const panel = api.addPanel({
id: 'panel_2',
component: 'default',
position: {
referenceGroup: panel2.group,
direction: 'left'
}
});
```
#### Relative to the container
```ts
const panel = api.addPanel({
id: 'panel_2',
component: 'default',
position: {
direction: 'right'
}
});
```
### Floating
You should specific the `floating` option which can be either `true` or an object describing the position of the floating group.
```ts
api.addPanel({
id: 'panel_1',
component: 'default',
floating: true,
});
api.addPanel({
id: 'panel_2',
component: 'default',
floating: { x: 10, y: 10, width: 300, height: 300 },
});
```

View File

@ -0,0 +1,28 @@
---
title: Move Panel
sidebar_position: 3
---
import { DocRef } from '@site/src/components/ui/reference/docRef';
This section describes how you can move a panel to another panel or group.
## Methods
<DocRef declaration="DockviewPanelApi" methods={["moveTo"]}/>
## Move a Panel
You can move a panel through the [Panel API](/docs/api/dockview/panelApi) and you can find out how to move a Group [here](/docs/core/groups/move).
```ts
panel.api.moveTo({ group, position, index });
```
An equivalent method for moving groups is avaliable on the group `api`.
```ts
const group = panel.api.group;
group.api.moveTo({ group, position });
```

View File

@ -0,0 +1,45 @@
---
title: Registering Panels
sidebar_position: 0
---
import { DocRef } from '@site/src/components/ui/reference/docRef';
This section describes how to register a panel.
You can register panels through the dock [option](/docs/api/dockview/options) `components`.
<FrameworkSpecific framework='React'>
<DocRef declaration="IDockviewReactProps" methods={['components']} />
</FrameworkSpecific>
<FrameworkSpecific framework='JavaScript'>
<DocRef declaration="DockviewComponentOptions" methods={['components']} />
</FrameworkSpecific>
<FrameworkSpecific framework='React'>
```tsx
const components = {
component_1: (props: IDockviewPanelProps) => {
const api: DockviewPanelApi = props.api;
const groupApi: DockviewGroupPanelApi = props.group.api;
const containerApi: DockviewApi = props.containerApi;
return <div>{/** logic */}</div>
},
component_2: (props: IDockviewPanelProps) => {
return <div>{/** logic */}</div>
}
}
return <DockviewReact components={components}/>
```
</FrameworkSpecific>
Each panel has an [api](/docs/api/dockview/panelApi) which is used to control specific
features on that individual panel.
The panel also has access the [group api](/docs/api/dockview/groupApi) and the container
[api](/docs/api/dockview/overview).

View File

@ -0,0 +1,29 @@
---
title: Remove Panel
sidebar_position: 4
---
import { DocRef } from '@site/src/components/ui/reference/docRef';
This section describes multiple ways to remove a panel.
## Remove a Panel using the Panel API
<DocRef declaration="DockviewPanelApi" methods={["close"]}/>
Calling `close` on the panel API is the easiest way to close a panel through code.
```ts
panel.api.close();
```
## Remove a Panel using the API
<DocRef declaration="DockviewApi" methods={["removePanel"]}/>
Firstly, you can retrieve a reference to the panel given it's id and then you can
pass that reference into `removePanel` to remove the panel.
```ts
const panel: IDockviewPanel = api.getPanel('myPanel');
api.removePanel(panel);
```

View File

@ -0,0 +1,132 @@
---
title: Rendering Panels
sidebar_postiion: 5
---
import { MultiFrameworkContainer } from '@site/src/components/ui/container';
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
If you are looking for information on how to render **iframes** in Dockview please go the the [iframes](/docs/advanced/iframe) section.
:::
When a panel is selected all other panels in that group are not visible. The API does expose methods to determine whether your panel is visible or not
and the panel instance only ever destroyed when removed however the question still remains, what to do with the partial DOM tree that makes up your panel and there are two options the dock can take:
1. (*onlyWhenVisible*) Remove the element from the DOM tree to make space for the new panel.
This will cause the element to loss any DOM-specific state such as scrollbar position and if you measure the size of any elements during this time you will mostly like see both a width and height of 0px,
this is also true for any active ResizeObservers.
```ts
api.addPanel({
id: 'my_unique_panel_id',
component: 'my_component',
renderer: 'always'
});
```
2. (*always*) Keep the DOM tree alive but hide it in order to allow the select panels content to show.
This approach will maintain any DOM-sepcific state you had and is essential if you require the native scrollbar position to be preserved.
```ts
api.addPanel({
id: 'my_unique_panel_id',
component: 'my_component',
renderer: 'onlyWhenVisible'
});
```
Both are valid use-cases therefore the dock allows you to choose your rendering mode, the default however is the first option since this is the most memory efficient solution.
> You can change the `defaultRenderer` in the Dock [Options](/docs/api/dockview/options).
:::info
The panel instance is only ever destroyed when it is removed from the dock allowing you to still run code associated with the panel when it is not visible.
The renderer only affects what happens to the DOM element.
:::
## Choose a Render Mode
```ts
api.addPanel({
id: 'my_unique_panel_id',
component: 'my_component',
renderer: 'always'
});
api.addPanel({
id: 'my_unique_panel_id',
component: 'my_component',
renderer: 'onlyWhenVisible'
});
```
## Live Example
<LiveExample framework="react" id="dockview/render-mode"/>
By default `DockviewReact` only adds to the DOM those panels that are visible,
if a panel is not the active tab and not shown the contents of the hidden panel will be removed from the DOM.
When a panel is in `onlyWhenVisible` render mode this only affects the contents within the DOM. The lifecycle of that panel instance is still maintained.
The React Components associated with each panel are only created once and will always exist for as long as the panel exists, hidden or not.
> e.g. This means that any hooks in those components will run whether the panel is visible or not which may lead to excessive background work depending
> on the panels implementation.
You can listen to the visiblity state of the panel and write additional logic to optimize your application if required, although this is an advanced case.
If you wanted to unmount the React Components when the panel is not visible you could create a Higher-Order-Component that listens to the panels
visiblity state and only renders the panel when visible.
```tsx title="Only rendering the React Component when the panel is visible, otherwise rendering a null React Component"
import { IDockviewPanelProps } from 'dockview';
import * as React from 'react';
function RenderWhenVisible(
component: React.FunctionComponent<IDockviewPanelProps>
) {
const HigherOrderComponent = (props: IDockviewPanelProps) => {
const [visible, setVisible] = React.useState<boolean>(
props.api.isVisible
);
React.useEffect(() => {
const disposable = props.api.onDidVisibilityChange((event) =>
setVisible(event.isVisible)
);
return () => {
disposable.dispose();
};
}, [props.api]);
if (!visible) {
return null;
}
return React.createElement(component, props);
};
return HigherOrderComponent;
}
```
```tsx
const components = { default: RenderWhenVisible(MyComponent) };
```
Toggling the checkbox you can see that when you only render those panels which are visible the underling React component is destroyed when it becomes hidden and re-created when it becomes visible.
<MultiFrameworkContainer
sandboxId="rendering-dockview"
react={RenderingDockview}
/>

View File

@ -0,0 +1,40 @@
---
title: Resizing
---
This section describes how to programatically resize a panel.
import LiveExample from '@site/src/components/ui/exampleFrame';
import { DocRef } from '@site/src/components/ui/reference/docRef';
<DocRef declaration="DockviewPanelApi" methods={['height', 'width', 'setSize', 'onDidDimensionsChange']} />
## Panel Resizing
Each dock contains groups and each group contains panels.
Logically a user may want to resize a panel but this really translates to resizing the group which contains that panel.
The panel resize methods are repeats of the same resize methods found on the group.
```tsx
// it's mandatory to provide either a height or a width, providing both is optional
props.api.setSize({
height: 100,
width: 200,
});
/**
* you could also resize the panels group, although not recommended due to the
* clunky syntax it does achieve the same result
*/
props.api.group.api.setSize({
height: 100,
width: 200,
});
```
You can see an example invoking both approaches below.
<LiveExample framework="react" id="dockview/resize"/>

View File

@ -0,0 +1,140 @@
---
title: Tabs
sidebar_position: 2
---
import { MultiFrameworkContainer } from '@site/src/components/ui/container';
import CustomHeadersDockview from '@site/sandboxes/customheader-dockview/src/app';
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';
This section describes how to implement custom tab renderers
## Register a Tab Component
<FrameworkSpecific framework='React'>
```tsx
const components = {
tab_1: (props: IDockviewPanelHeaderProps) => {
const api: DockviewPanelApi = props.api;
const containerApi: DockviewApi = props.containerApi;
return <div>{/** logic */}</div>
},
tab_2: (props: IDockviewPanelHeaderProps) => {
return <div>{/** logic */}</div>
}
};
return <DockviewReact tabComponents={tabComponents}/>
```
</FrameworkSpecific>
<FrameworkSpecific framework='JavaScript'>
not implemented
</FrameworkSpecific>
## Default Tab Renderer
<FrameworkSpecific framework='React'>
```jsx
const CustomTabRenderer = (props: IDockviewPanelHeaderProps) => {
const api: DockviewPanelApi = props.api;
const containerApi: DockviewApi = props.containerApi;
return <div>{/** logic */}</div>
}
return <DockviewReact defaultTabRenderer={CustomTabRenderer}/>
```
</FrameworkSpecific>
<FrameworkSpecific framework='JavaScript'>
not implemented
</FrameworkSpecific>
## Accessing Custom Panel Parameters
You can provide a generic type that matches the structure of the epxected custom panels parameters
to provide type-hints for the panel parameters which can be accessed via the `params` option.
<FrameworkSpecific framework='React'>
```jsx
type MyParameters = { my_value: number };
const MyTab = (props: IDockviewPanelHeaderProps<MyParameters>) => {
const value: number = props.params.my_value;
return <div>{/** logic */}</div>
}
```
</FrameworkSpecific>
## Extend the Default Tab Implementation
If you only want to make minor changes to the tab rendering you may be able to use the
default implementation as a base. This could include:
- Hiding the close button
- Attaching additional event listeners
<FrameworkSpecific framework='React'>
```tsx
import { IDockviewPanelHeaderProps, DockviewDefaultTab } from 'dockview';
const MyCustomTab = (props: IDockviewPanelHeaderProps) => {
const onContextMenu = (event: React.MouseEvent) => {
event.preventDefault();
alert('context menu');
};
return <DockviewDefaultTab onContextMenu={onContextMenu} hideClose={true} {...props} />;
};
```
</FrameworkSpecific>
As a simple example the below attaches a custom event handler for the context menu on all tabs as a default tab renderer
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}
/>
## Full Width Tab
When a group has only one single tab you may want that tab to take the full width.
<FrameworkSpecific framework='React'>
<DocRef declaration="IDockviewReactProps" methods={['singleTabMode']} />
</FrameworkSpecific>
<FrameworkSpecific framework='JavaScript'>
<DocRef declaration="DockviewComponentOptions" methods={['singleTabMode']} />
</FrameworkSpecific>
```tsx
return <DockviewReactComponent singleTabMode="fullwidth" />
```
<MultiFrameworkContainer
sandboxId="fullwidthtab-dockview"
react={DockviewNative}
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

@ -0,0 +1,46 @@
---
title: Update Panel
sidebar_position: 2
---
import { DocRef } from '@site/src/components/ui/reference/docRef';
import LiveExample from '@site/src/components/ui/exampleFrame';
This section describes how to update the parameters of a panel.
:::warning
**Use this feature sparingly**: Anything you assign to the `params` options of a panel will be saved when calling `api.toJSON()`.
Only use this to store small amounts of static view data.
**Do not** use this to store application state or dynamic panel state.
:::
## Methods
<DocRef declaration="DockviewPanelApi" methods={['updateParameters']} />
## Updating parameters
:::info
If you want to set initial parameters when adding a panel see the [Add Panel](/docs/core/panels/add) section.
:::
You can update a panel through the [Panel API](/docs/api/dockview/panelApi).
```ts
panel.api.updateParameters({
keyA: 'anotherValueA',
keyB: 'valueB',
});
```
To delete a parameter you should pass a value of `undefined`.
```ts
panel.api.updateParameters({
keyA: undefined,
});
```
## Live Example
<LiveExample framework="react" id="dockview/update-parameters"/>

View File

@ -0,0 +1,20 @@
---
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.
If you are using a relative height such as `100%` you will need a child container
with the appropiate `overflow` value to allow for scrollbars.
## Live Examples
The following example contains three views:
- **Panel 1** (`height: 100%`): No scrollbar appears and the content is clipped.
- **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"/>

View File

@ -0,0 +1,6 @@
{
"position": 3,
"label": "State",
"collapsible": true,
"collapsed": true
}

View File

@ -0,0 +1,47 @@
---
title: Loading State
---
import { MultiFrameworkContainer } from '@site/src/components/ui/container';
import LiveExample from '@site/src/components/ui/exampleFrame';
import { DocRef } from '@site/src/components/ui/reference/docRef';
This section described loading a dock layout.
<DocRef declaration="DockviewApi" methods={['fromJSON', 'onDidLayoutFromJSON']} />
## Load A Layout
To load a layout you should a pass a valid object to `fromJSON`. If you try to load an invalid or corrupted layout the dock will throw an Error and the dock will reset gracefully ready
for another attempt with a valid object.
You could load a previously saved layout from local storage for example.
```tsx
const onReady = (event: DockviewReadyEvent) => {
let success = false;
const mySerializedLayout = localStorage.getItem('my_layout');
if (mySerializedLayout) {
try {
const layout = JSON.parse(mySerializedLayout);
event.api.fromJSON(layout);
success = true;
} catch (err) {
// log the error
}
}
if (!success) {
// perhap load a default layout?
}
};
return <DockviewComponent onReady={onReady}/>;
```
# Live Example
<LiveExample framework="react" id="dockview/layout"/>

View File

@ -0,0 +1,43 @@
---
title: Saving State
---
import { MultiFrameworkContainer } from '@site/src/components/ui/container';
import LiveExample from '@site/src/components/ui/exampleFrame';
import { DocRef } from '@site/src/components/ui/reference/docRef';
This section describes how to serialize a dockview instance.
<DocRef declaration="DockviewApi" methods={['toJSON', 'onDidLayoutChange']} />
To retrieve the current state of the dock call `toJSON()`.
You can listen to the event `onDidlayoutChange` to determine when the layout has changed.
```tsx
const [api, setApi] = React.useState<DockviewApi>();
React.useEffect(() => {
if(!api) {
return;
}
const disposable = api.onDidLayoutChange(() => {
const layout: SerializedDockview = api.toJSON();
localStorage.setItem('my_layout', JSON.stringify(layout));
});
return () => disposable.dispose();
}, [api]);
const onReady = (event: DockviewReadyEvent) => {
setApi(event.api);
}
return <DockviewComponent onReady={onReady}/>
```
# Live Example
<LiveExample framework="react" id="dockview/layout"/>

View File

@ -0,0 +1,31 @@
---
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.
When the dock is empty or a group has no panels (an empty group) you can render some fallback
content which is refered to as a `watermark`. Both are controlled through the same provided component.
## Options
The following properties can be set to configure the behaviours of floating groups.
<FrameworkSpecific framework='React'>
<DocRef declaration="IDockviewReactProps" methods={['watermarkComponent']} />
</FrameworkSpecific>
<FrameworkSpecific framework='JavaScript'>
<DocRef declaration="DockviewComponentOptions"
methods={['watermarkComponent', 'watermarkFrameworkComponent', 'frameworkComponentFactory']}
/>
</FrameworkSpecific>
## Live Examples
<LiveExample framework="react" id="dockview/watermark"/>

View File

@ -1,6 +1,7 @@
---
sidebar_position: 0
description: A zero dependency layout manager supporting ReactJS and Vanilla TypeScript
title: Introduction
---
import { MultiFrameworkContainer } from '@site/src/components/ui/container';
@ -8,11 +9,9 @@ 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 DockviewDemo from '@site/sandboxes/demo-dockview/src/app';
import Link from '@docusaurus/Link';
# Introduction
**dockview** is a zero dependency layout manager that supports tab, grids and splitviews.
## Quick start
@ -36,11 +35,11 @@ There are 4 components you may want to use:
<h2>Dockview</h2>
</Link>
<MultiFrameworkContainer
{/* <MultiFrameworkContainer
height={500}
sandboxId="demo-dockview"
react={DockviewDemo}
/>
/> */}
<Link to="./components/splitview">
<h2>Splitview</h2>

View File

@ -0,0 +1,6 @@
{
"position": 0,
"label": "Get Started",
"collapsible": true,
"collapsed": true
}

View File

@ -1,11 +1,10 @@
---
sidebar_position: 3
sidebar_position: 2
description: Contributing
title: Contributing
---
# Contributing
# Project description
## Project description
Pre-requisites: Node >=18, Yarn

View File

@ -0,0 +1,23 @@
---
id: installation
title: Installation
sidebar_position: 0
---
Learn how to install Dockview for a selection of frameworks.
<FrameworkSpecific framework='JavaScript'>
Firstly, install the `dockvire-core` library:
```sh
npm install dockview-core
```
</FrameworkSpecific>
<FrameworkSpecific framework='React'>
Firstly, install the `dockview` library:
```sh
npm install dockview
```
</FrameworkSpecific>

View File

@ -0,0 +1,69 @@
---
sidebar_position: 1
description: Theming Dockview Components
title: Theme
---
import { CSSVariablesTable, ThemeTable } from '@site/src/components/cssVariables';
Theming is controlled through CSS and is highly customizable.
Firstly, you should import `dockview.css`:
<FrameworkSpecific framework='JavaScript'>
```css
@import './node_modules/dockview-core/dist/styles/dockview.css';
```
</FrameworkSpecific>
<FrameworkSpecific framework='React'>
```css
@import './node_modules/dockview/dist/styles/dockview.css';
```
</FrameworkSpecific>
## Provided themes
`dockview` comes with a number of themes which are all CSS classes and can be found [here](https://github.com/mathuo/dockview/blob/master/packages/dockview-core/src/theme.scss).
To use a `dockview` theme the CSS must encapsulate the component. The current list of themes is:
<ThemeTable/>
:::info
The source code for all themes can be found [here](https://github.com/mathuo/dockview/blob/master/packages/dockview-core/src/theme.scss).
:::
## Customizing Theme
The provided themes are controlled primarily through a long list of CSS variables which can be modified by the user either entirely for a new theme
or partial for a modification to an existing theme.
<CSSVariablesTable />
## Extending Theme
You can extends existing themes or create new themes.
As an example if you wanted to extend the **dockview-theme-abyss** theme to dislay a within the tabs container you
may try:
```css
.dockview-theme-abyss {
.groupview {
&.active-group {
> .tabs-and-actions-container {
border-bottom: 2px solid var(--dv-activegroup-visiblepanel-tab-background-color);
}
}
&.inactive-group {
> .tabs-and-actions-container {
border-bottom: 2px solid var(--dv-inactivegroup-visiblepanel-tab-background-color);
}
}
}
}
```

View File

@ -1,85 +0,0 @@
---
sidebar_position: 1
description: Theming Dockview Components
---
# Theme
## Introduction
`dockview` requires some CSS to work correctly.
The CSS is exported as one file under [`dockview/dict/styles/dockview.css`](https://unpkg.com/browse/dockview@latest/dist/styles/dockview.css)
and should be imported at some point in your application
```css title="Example import with .css file"
@import './node_modules/dockview/dist/styles/dockview.css';
```
## Provided themes
`dockview` comes with a number of themes which are all CSS classes and can be found [here](https://github.com/mathuo/dockview/blob/master/packages/dockview-core/src/theme.scss).
To use a `dockview` theme the CSS must encapsulate the component. The current list of themes is:
- `dockview-theme-dark`
- `dockview-theme-light`
- `dockview-theme-vs`
- `dockview-theme-abyss`
- `dockview-theme-dracula`
- `dockview-theme-replit`
## Customizing Theme
`dockview` supports theming through the use of css properties.
You can view the built-in themes at [`dockview/src/theme.scss`](https://github.com/mathuo/dockview/blob/master/packages/dockview/src/theme.scss)
and are free to build your own themes based on these css properties.
| CSS Property | Description |
| ---------------------------------------------------- | ----------- |
| --dv-paneview-active-outline-color | |
| --dv-tabs-and-actions-container-font-size | |
| --dv-tabs-and-actions-container-height | |
| --dv-tab-close-icon | |
| --dv-drag-over-background-color | |
| --dv-drag-over-border-color | |
| --dv-tabs-container-scrollbar-color | |
| | |
| --dv-group-view-background-color | |
| | |
| --dv-tabs-and-actions-container-background-color | |
| | |
| --dv-activegroup-visiblepanel-tab-background-color | |
| --dv-activegroup-hiddenpanel-tab-background-color | |
| --dv-inactivegroup-visiblepanel-tab-background-color | |
| --dv-inactivegroup-hiddenpanel-tab-background-color | |
| --dv-tab-divider-color | |
| | |
| --dv-activegroup-visiblepanel-tab-color | |
| --dv-activegroup-hiddenpanel-tab-color | |
| --dv-inactivegroup-visiblepanel-tab-color | |
| --dv-inactivegroup-hiddenpanel-tab-color | |
| | |
| --dv-separator-border | |
| --dv-paneview-header-border-color | |
| | |
| --dv-icon-hover-background-color | |
| --dv-floating-box-shadow | |
| --dv-active-sash-color | |
| --dv-background-color | |
You can further customise the theme through adjusting class properties but this is up you.
For example if you wanted to add a bottom border to the tab container for an active group in the `DockviewReact` component you could write:
```css title="Additional CSS to show a bottom border on active groups"
.groupview {
&.active-group {
> .tabs-and-actions-container {
border-bottom: 2px solid var(--dv-activegroup-visiblepanel-tab-background-color);
}
}
&.inactive-group {
> .tabs-and-actions-container {
border-bottom: 2px solid var(--dv-inactivegroup-visiblepanel-tab-background-color);
}
}
}
```

View File

@ -3,7 +3,26 @@
const { themes } = require('prism-react-renderer');
const lightCodeTheme = themes.nightOwlLight;
const darkCodeTheme = themes.vsDark;
const darkCodeTheme = themes.palenight;
// dracula
// duotoneDark
// duotoneLight
// github
// jettwaveDark
// jettwaveLight
// nightOwl
// nightOwlLight
// oceanicNext
// okaidia
// oneDark
// oneLight
// palenight
// shadesOfPurple
// synthwave84
// ultramin
// vsDark
// vsLight
const path = require('path');
@ -101,7 +120,7 @@ const config = {
'https://github.com/facebook/docusaurus/tree/main/packages/create-docusaurus/templates/shared/',
},
theme: {
customCss: require.resolve('./src/css/custom.css'),
customCss: require.resolve('./src/css/custom.scss'),
},
gtag: process.env.CI
? {
@ -137,6 +156,11 @@ const config = {
].join(' ,'),
},
],
docs: {
sidebar: {
autoCollapseCategories: true,
},
},
navbar: {
title: 'Dockview',
logo: {
@ -146,24 +170,33 @@ const config = {
items: [
{
type: 'doc',
docId: 'index',
docId: 'overview/getStarted/installation',
position: 'left',
label: 'Docs',
},
{ to: '/blog', label: 'Blog', position: 'left' },
{
to: 'https://dockview.dev/typedocs',
label: 'TSDoc',
type: 'docSidebar',
position: 'left',
sidebarId: 'api',
label: 'API',
},
{ to: '/blog', label: 'Blog', position: 'left' },
{ to: '/demo', label: 'Demo', position: 'left' },
// {
// to: 'https://dockview.dev/typedocs',
// label: 'TSDoc',
// position: 'left',
// },
{
type: 'docsVersionDropdown',
position: 'right',
},
{
href: 'https://github.com/mathuo/dockview',
label: 'GitHub',
position: 'right',
className: 'header-github-link',
'aria-label': 'GitHub repository',
},
],
},
@ -227,6 +260,9 @@ const config = {
id: 'announcementBar', // Increment on change
content: `⭐️ If you like Dockview, give it a star on <a target="_blank" rel="noopener noreferrer" href="https://github.com/mathuo/dockview">GitHub</a>`,
},
tableOfContents: {
maxHeadingLevel: 5,
},
}),
};

20
packages/docs/locate.js Normal file
View File

@ -0,0 +1,20 @@
const fs = require('fs');
const path = require('path');
function* readAllFiles(dir) {
const files = fs.readdirSync(dir, { withFileTypes: true });
for (const file of files) {
if (file.isDirectory()) {
yield* readAllFiles(path.join(dir, file.name));
} else {
yield path.join(dir, file.name);
}
}
}
console.log(
Array.from(
readAllFiles(path.join(__dirname, '..', 'dockview-core', 'src'))
).filter((file) => file.endsWith('.scss'))
);

View File

@ -6,6 +6,7 @@
"build": "docusaurus build",
"clear": "docusaurus clear",
"start": "docusaurus start",
"swizzle": "docusaurus swizzle @docusaurus/theme-classic",
"docs:version": "docusaurus docs:version",
"typecheck": "tsc"
},
@ -22,13 +23,15 @@
]
},
"dependencies": {
"@docusaurus/core": "^3.0.1",
"@docusaurus/module-type-aliases": "^3.0.1",
"@docusaurus/preset-classic": "^3.0.1",
"@docusaurus/core": "^3.1.1",
"@docusaurus/module-type-aliases": "^3.1.1",
"@docusaurus/preset-classic": "^3.1.1",
"@mdx-js/react": "^3.0.0",
"@minoru/react-dnd-treeview": "^3.4.4",
"@radix-ui/react-icons": "^1.3.0",
"@radix-ui/react-popover": "^1.0.7",
"ag-grid-community": "^31.0.2",
"ag-grid-react": "^31.0.2",
"axios": "^1.6.3",
"clsx": "^2.1.0",
"dockview": "^1.9.2",

View File

@ -1,649 +0,0 @@
import {
DockviewDefaultTab,
DockviewReact,
DockviewReadyEvent,
IDockviewPanelHeaderProps,
IDockviewPanelProps,
IDockviewHeaderActionsProps,
DockviewPanelApi,
DockviewPanelRenderer,
DockviewGroupLocation,
DockviewApi,
} from 'dockview';
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { v4 } from 'uuid';
import './app.scss';
interface PanelApiMetadata {
isActive: {
value: boolean;
count: number;
};
isVisible: {
value: boolean;
count: number;
};
isHidden: {
value: boolean;
count: number;
};
renderer: {
value: DockviewPanelRenderer;
count: number;
};
isGroupActive: {
value: boolean;
count: number;
};
groupChanged: {
count: number;
};
location: {
value: DockviewGroupLocation;
count: number;
};
didFocus: {
count: number;
};
dimensions: {
count: number;
height: number;
width: number;
};
}
function usePanelApiMetadata(api: DockviewPanelApi): PanelApiMetadata {
const [state, setState] = React.useState<PanelApiMetadata>({
isActive: { value: api.isActive, count: 0 },
isVisible: { value: api.isVisible, count: 0 },
isHidden: { value: api.isHidden, count: 0 },
renderer: { value: api.renderer, count: 0 },
isGroupActive: { value: api.isGroupActive, count: 0 },
groupChanged: { count: 0 },
location: { value: api.location, count: 0 },
didFocus: { count: 0 },
dimensions: { count: 0, height: api.height, width: api.width },
});
React.useEffect(() => {
const d1 = api.onDidActiveChange((event) => {
setState((_) => ({
..._,
isActive: {
value: event.isActive,
count: _.isActive.count + 1,
},
}));
});
const d2 = api.onDidActiveGroupChange((event) => {
setState((_) => ({
..._,
isGroupActive: {
value: event.isActive,
count: _.isGroupActive.count + 1,
},
}));
});
const d3 = api.onDidDimensionsChange((event) => {
setState((_) => ({
..._,
dimensions: {
count: _.dimensions.count + 1,
height: event.height,
width: event.width,
},
}));
});
const d4 = api.onDidFocusChange((event) => {
setState((_) => ({
..._,
didFocus: {
count: _.didFocus.count + 1,
},
}));
});
const d5 = api.onDidGroupChange((event) => {
setState((_) => ({
..._,
groupChanged: {
count: _.groupChanged.count + 1,
},
}));
});
const d6 = api.onDidHiddenChange((event) => {
setState((_) => ({
..._,
isHidden: {
value: event.isHidden,
count: _.isHidden.count + 1,
},
}));
});
const d7 = api.onDidLocationChange((event) => {
setState((_) => ({
..._,
location: {
value: event.location,
count: _.location.count + 1,
},
}));
});
const d8 = api.onDidRendererChange((event) => {
setState((_) => ({
..._,
renderer: {
value: event.renderer,
count: _.renderer.count + 1,
},
}));
});
const d9 = api.onDidVisibilityChange((event) => {
setState((_) => ({
..._,
isVisible: {
value: event.isVisible,
count: _.isVisible.count + 1,
},
}));
});
return () => {
d1.dispose();
d2.dispose();
d3.dispose();
d4.dispose();
d5.dispose();
d6.dispose();
d7.dispose();
d8.dispose();
d9.dispose();
};
}, [api]);
return state;
}
const components = {
default: (props: IDockviewPanelProps) => {
const metadata = usePanelApiMetadata(props.api);
return (
<div
style={{
height: '100%',
overflow: 'auto',
color: 'white',
position: 'relative',
}}
>
<pre style={{ fontSize: '11px' }}>
{JSON.stringify(metadata, null, 4)}
</pre>
<span
style={{
position: 'absolute',
top: '50%',
left: '50%',
transform: 'translate(-50%,-50%)',
pointerEvents: 'none',
fontSize: '42px',
opacity: 0.5,
}}
>
{props.api.title}
</span>
</div>
);
},
};
const headerComponents = {
default: (props: IDockviewPanelHeaderProps) => {
const onContextMenu = (event: React.MouseEvent) => {
event.preventDefault();
alert('context menu');
};
return <DockviewDefaultTab onContextMenu={onContextMenu} {...props} />;
},
};
const Icon = (props: {
icon: string;
title?: string;
onClick?: (event: React.MouseEvent) => void;
}) => {
return (
<div title={props.title} className="action" onClick={props.onClick}>
<span
style={{ fontSize: 'inherit' }}
className="material-symbols-outlined"
>
{props.icon}
</span>
</div>
);
};
const groupControlsComponents: Record<string, React.FC> = {
panel_1: () => {
return <Icon icon="file_download" />;
},
};
const RightControls = (props: IDockviewHeaderActionsProps) => {
const Component = React.useMemo(() => {
if (!props.isGroupActive || !props.activePanel) {
return null;
}
return groupControlsComponents[props.activePanel.id];
}, [props.isGroupActive, props.activePanel]);
const [isMaximized, setIsMaximized] = React.useState<boolean>(
props.containerApi.hasMaximizedGroup()
);
const [isPopout, setIsPopout] = React.useState<boolean>(
props.api.location.type === 'popout'
);
React.useEffect(() => {
const disposable = props.containerApi.onDidMaximizedGroupChange(() => {
setIsMaximized(props.containerApi.hasMaximizedGroup());
});
const disposable2 = props.api.onDidLocationChange(() => {
setIsPopout(props.api.location.type === 'popout');
});
return () => {
disposable.dispose();
disposable2.dispose();
};
}, [props.containerApi]);
const onClick = () => {
if (props.containerApi.hasMaximizedGroup()) {
props.containerApi.exitMaximizedGroup();
} else {
props.activePanel?.api.maximize();
}
};
const onClick2 = () => {
if (props.api.location !== 'popout') {
props.containerApi.addPopoutGroup(props.group);
} else {
props.api.moveTo({ position: 'right' });
}
};
return (
<div
className="group-control"
style={{
display: 'flex',
alignItems: 'center',
padding: '0px 8px',
height: '100%',
color: 'var(--dv-activegroup-visiblepanel-tab-color)',
}}
>
{props.isGroupActive && <Icon icon="star" />}
{Component && <Component />}
<Icon
title={isPopout ? 'Close Window' : 'Open In New Window'}
icon={isPopout ? 'close_fullscreen' : 'open_in_new'}
onClick={onClick2}
/>
{!isPopout && (
<Icon
title={isMaximized ? 'Minimize View' : 'Maximize View'}
icon={isMaximized ? 'collapse_content' : 'expand_content'}
onClick={onClick}
/>
)}
</div>
);
};
let counter = 0;
const LeftControls = (props: IDockviewHeaderActionsProps) => {
const onClick = () => {
props.containerApi.addPanel({
id: `id_${Date.now().toString()}`,
component: 'default',
title: `Tab ${counter++}`,
position: {
referenceGroup: props.group,
},
});
};
return (
<div
className="group-control"
style={{
display: 'flex',
alignItems: 'center',
padding: '0px 8px',
height: '100%',
color: 'var(--dv-activegroup-visiblepanel-tab-color)',
}}
>
<Icon onClick={onClick} icon="add" />
</div>
);
};
const PrefixHeaderControls = (props: IDockviewHeaderActionsProps) => {
return (
<div
className="group-control"
style={{
display: 'flex',
alignItems: 'center',
padding: '0px 8px',
height: '100%',
color: 'var(--dv-activegroup-visiblepanel-tab-color)',
}}
>
<Icon icon="Menu" />
</div>
);
};
const DockviewDemo = (props: { theme?: string }) => {
const [logLines, setLogLines] = React.useState<any[]>([]);
const [panels, setPanels] = React.useState<string[]>([]);
const [groups, setGroups] = React.useState<string[]>([]);
const [api, setApi] = React.useState<DockviewApi>();
const [activePanel, setActivePanel] = React.useState<string>();
const [activeGroup, setActiveGroup] = React.useState<string>();
const onReady = (event: DockviewReadyEvent) => {
setApi(event.api);
event.api.onDidAddPanel((event) => {
setPanels((_) => [..._, event.id]);
setLogLines((line) => [`Panel Added ${event.id}`, ...line]);
});
event.api.onDidActivePanelChange((event) => {
setActivePanel(event?.id);
setLogLines((line) => [`Panel Activated ${event?.id}`, ...line]);
});
event.api.onDidRemovePanel((event) => {
setPanels((_) => {
const next = [..._];
next.splice(
next.findIndex((x) => x === event.id),
1
);
return next;
});
setLogLines((line) => [`Panel Removed ${event.id}`, ...line]);
});
event.api.onDidAddGroup((event) => {
setGroups((_) => [..._, event.id]);
setLogLines((line) => [`Group Added ${event.id}`, ...line]);
});
event.api.onDidRemoveGroup((event) => {
setGroups((_) => {
const next = [..._];
next.splice(
next.findIndex((x) => x === event.id),
1
);
return next;
});
setLogLines((line) => [`Group Removed ${event.id}`, ...line]);
});
event.api.onDidActiveGroupChange((event) => {
setActiveGroup(event?.id);
setLogLines((line) => [`Group Activated ${event?.id}`, ...line]);
});
const panel1 = event.api.addPanel({
id: 'panel_1',
component: 'default',
title: 'Panel 1',
});
event.api.addPanel({
id: 'panel_2',
component: 'default',
title: 'Panel 2',
position: { referencePanel: panel1 },
});
event.api.addPanel({
id: 'panel_3',
component: 'default',
title: 'Panel 3',
position: { referencePanel: panel1 },
});
const panel4 = event.api.addPanel({
id: 'panel_4',
component: 'default',
title: 'Panel 4',
position: { referencePanel: panel1, direction: 'right' },
});
const panel5 = event.api.addPanel({
id: 'panel_5',
component: 'default',
title: 'Panel 5',
position: { referencePanel: panel4 },
});
const panel6 = event.api.addPanel({
id: 'panel_6',
component: 'default',
title: 'Panel 6',
position: { referencePanel: panel5, direction: 'below' },
});
const panel7 = event.api.addPanel({
id: 'panel_7',
component: 'default',
title: 'Panel 7',
position: { referencePanel: panel6, direction: 'left' },
});
event.api.addPanel({
id: 'panel8',
component: 'default',
title: 'Panel 8',
position: { referencePanel: panel7, direction: 'below' },
});
panel1.api.setActive();
};
const onAddPanel = () => {
api?.addPanel({
id: `id_${Date.now().toString()}`,
component: 'default',
title: `Tab ${counter++}`,
});
};
const onAddGroup = () => {
api?.addGroup();
};
return (
<div
style={{ height: '100%', display: 'flex', flexDirection: 'column' }}
>
<div>
<div
style={{
display: 'flex',
height: '25px',
padding: '2px 0px',
}}
>
<button onClick={onAddPanel}>Add Panel</button>
<button onClick={onAddGroup}>Add Group</button>
</div>
<div
style={{
display: 'flex',
height: '25px',
padding: '2px 0px',
}}
>
{panels.map((x) => {
const onClick = () => {
api?.getPanel(x)?.focus();
};
return (
<>
<button
onClick={onClick}
style={{
minWidth: '50px',
border: 'none',
margin: '0px 2px',
padding: '0px 2px',
backgroundColor:
activePanel === x
? 'blueviolet'
: 'dodgerblue',
borderRadius: '2px',
}}
>
{x}
</button>
<button
onClick={() => {
const panel = api?.getPanel(x);
if (panel) {
api?.addFloatingGroup(panel);
}
}}
>
float
</button>
<button
onClick={() => {
const panel = api?.getPanel(x);
if (panel) {
api?.addPopoutGroup(panel);
}
}}
>
pop
</button>
</>
);
})}
</div>
<div
style={{
display: 'flex',
height: '25px',
padding: '2px 0px',
}}
>
{groups.map((x) => {
const onClick = () => {
api?.getGroup(x)?.focus();
};
return (
<>
<button
onClick={onClick}
style={{
minWidth: '50px',
border: 'none',
margin: '0px 2px',
padding: '0px 2px',
backgroundColor:
activeGroup === x
? 'blueviolet'
: 'dodgerblue',
borderRadius: '2px',
}}
>
{x}
</button>
<button
onClick={() => {
const panel = api?.getGroup(x);
if (panel) {
api?.addFloatingGroup(panel);
}
}}
>
float
</button>
<button
onClick={() => {
const panel = api?.getGroup(x);
if (panel) {
api?.addPopoutGroup(panel);
}
}}
>
pop
</button>
</>
);
})}
</div>
</div>
<div style={{ flexGrow: 1 }}>
<DockviewReact
components={components}
defaultTabComponent={headerComponents.default}
rightHeaderActionsComponent={RightControls}
leftHeaderActionsComponent={LeftControls}
prefixHeaderActionsComponent={PrefixHeaderControls}
onReady={onReady}
className={props.theme || 'dockview-theme-abyss'}
/>
</div>
<div
style={{
height: '200px',
backgroundColor: 'black',
color: 'white',
overflow: 'auto',
}}
>
{logLines.map((line, i) => {
return (
<div key={i}>
<span
style={{
display: 'inline-block',
width: '30px',
color: 'gray',
borderRight: '1px solid gray',
marginRight: '4px',
}}
>
{logLines.length - i}
</span>
<span>{line}</span>
</div>
);
})}
</div>
</div>
);
};
export default DockviewDemo;

View File

@ -0,0 +1,34 @@
{
"name": "example-app-dockview",
"description": "",
"keywords": [
"dockview"
],
"version": "1.0.0",
"main": "src/index.tsx",
"dependencies": {
"ag-grid-community": "^31.0.2",
"ag-grid-react": "^31.0.2",
"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,17 @@
.my-custom-tab {
padding: 0px 8px;
width: 100%;
display: flex;
height: 100%;
align-items: center;
background-color: var(--dv-tabs-and-actions-container-background-color);
.my-custom-tab-icon {
font-size: 16px;
&:hover {
border-radius: 2px;
background-color: var(--dv-icon-hover-background-color);
}
}
}

View File

@ -0,0 +1,90 @@
import {
DockviewReact,
DockviewReadyEvent,
IDockviewPanelProps,
IDockviewPanelHeaderProps,
} from 'dockview';
import * as React from 'react';
import './app.scss';
import { AgGridReact } from 'ag-grid-react';
import { ColDef } from 'ag-grid-community';
import 'ag-grid-community/styles/ag-grid.css';
import 'ag-grid-community/styles/ag-theme-balham.css';
const data = new Array(1000).fill(0).map((_, i) => {
return {
index: i,
a: Math.random() * 100,
b: Math.random() * 100,
c: Math.random() * 100,
d: Math.random() * 100,
};
});
const columnDefs: ColDef[] = [
{
field: 'a',
},
{
field: 'b',
},
{
field: 'c',
},
{
field: 'd',
},
];
const components = {
default: (props: IDockviewPanelProps<{ title: string; x?: number }>) => {
return (
<div
style={{
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
color: 'white',
height: '100%',
}}
>
<span>{`${props.params.title}`}</span>
{props.params.x && <span>{` ${props.params.x}`}</span>}
</div>
);
},
grid: () => {
return (
<div className="ag-theme-balham-dark" style={{ height: '100%' }}>
<AgGridReact rowData={data} columnDefs={columnDefs} />
</div>
);
},
};
const DockviewComponent = (props: { theme?: string }) => {
const onReady = (event: DockviewReadyEvent) => {
const panel1 = event.api.addPanel({
id: 'panel_1',
component: 'default',
});
event.api.addPanel({
id: 'panel_2',
component: 'grid',
renderer: 'always',
});
};
return (
<DockviewReact
onReady={onReady}
components={components}
className={`${props.theme || 'dockview-theme-abyss'}`}
/>
);
};
export default DockviewComponent;

View File

@ -1,5 +1,5 @@
{
"name": "floatinggroup-dockview",
"name": "dockview.constraints",
"description": "",
"keywords": [
"dockview"

View File

@ -1,5 +1,5 @@
{
"name": "demo-dockview",
"name": "dockview.demo",
"description": "",
"keywords": [
"dockview"
@ -31,4 +31,4 @@
"not ie <= 11",
"not op_mini all"
]
}
}

View File

@ -0,0 +1,84 @@
.group-control {
.action {
padding: 4px;
display: flex;
align-items: center;
justify-content: center;
box-sizing: border-box;
font-size: 18px;
cursor: pointer;
&:hover {
border-radius: 2px;
background-color: var(--dv-icon-hover-background-color);
}
}
}
.data-table {
table {
font-size: 11px;
th {
padding: 0px 8px;
}
}
}
.action-container {
display: flex;
padding: 4px 0px;
overflow: auto;
button {
height: 25px;
display: flex;
align-items: center;
justify-content: center;
background-color: #1c254a;
border: none;
cursor: pointer;
outline: 1px solid #4c65d4;
&:hover {
background-color: #222e62;
}
}
.text-button {
margin: 0px 4px;
}
.button-action {
margin: 0px 4px;
// display: flex;
.selected {
background-color: #4864dc;
}
.demo-button {
min-width: 50px;
padding: 0px 2px;
border-radius: 0px;
display: flex;
flex-grow: 1;
align-items: center;
outline: 1px solid #4c65d4;
}
.demo-icon-button {
outline: 1px solid #4c65d4;
flex-grow: 1;
display: flex;
align-items: center;
border-radius: 0px;
padding: 0px 4px;
border: none;
cursor: pointer;
span {
font-size: 16px;
}
}
}
}

View File

@ -0,0 +1,213 @@
import {
DockviewDefaultTab,
DockviewReact,
DockviewReadyEvent,
IDockviewPanelHeaderProps,
IDockviewPanelProps,
DockviewApi,
} from 'dockview';
import * as React from 'react';
import './app.scss';
import { defaultConfig } from './defaultLayout';
import { GridActions } from './gridActions';
import { PanelActions } from './panelActions';
import { GroupActions } from './groupActions';
import { LeftControls, PrefixHeaderControls, RightControls } from './controls';
import { Table, usePanelApiMetadata } from './debugPanel';
const components = {
default: (props: IDockviewPanelProps) => {
const metadata = usePanelApiMetadata(props.api);
return (
<div
style={{
height: '100%',
overflow: 'auto',
color: 'white',
position: 'relative',
}}
>
<Table data={metadata} />
<span
style={{
position: 'absolute',
top: '50%',
left: '50%',
transform: 'translate(-50%,-50%)',
pointerEvents: 'none',
fontSize: '42px',
opacity: 0.5,
}}
>
{props.api.title}
</span>
</div>
);
},
iframe: () => {
return (
<iframe
style={{
width: '100%',
height: '100%',
}}
src="https://dockview.dev"
/>
);
},
};
const headerComponents = {
default: (props: IDockviewPanelHeaderProps) => {
const onContextMenu = (event: React.MouseEvent) => {
event.preventDefault();
alert('context menu');
};
return <DockviewDefaultTab onContextMenu={onContextMenu} {...props} />;
},
};
const DockviewDemo = (props: { theme?: string }) => {
const [logLines, setLogLines] = React.useState<any[]>([]);
const [panels, setPanels] = React.useState<string[]>([]);
const [groups, setGroups] = React.useState<string[]>([]);
const [api, setApi] = React.useState<DockviewApi>();
const [activePanel, setActivePanel] = React.useState<string>();
const [activeGroup, setActiveGroup] = React.useState<string>();
const onReady = (event: DockviewReadyEvent) => {
setApi(event.api);
event.api.onDidAddPanel((event) => {
setPanels((_) => [..._, event.id]);
setLogLines((line) => [`Panel Added ${event.id}`, ...line]);
});
event.api.onDidActivePanelChange((event) => {
setActivePanel(event?.id);
setLogLines((line) => [`Panel Activated ${event?.id}`, ...line]);
});
event.api.onDidRemovePanel((event) => {
setPanels((_) => {
const next = [..._];
next.splice(
next.findIndex((x) => x === event.id),
1
);
return next;
});
setLogLines((line) => [`Panel Removed ${event.id}`, ...line]);
});
event.api.onDidAddGroup((event) => {
setGroups((_) => [..._, event.id]);
setLogLines((line) => [`Group Added ${event.id}`, ...line]);
});
event.api.onDidRemoveGroup((event) => {
setGroups((_) => {
const next = [..._];
next.splice(
next.findIndex((x) => x === event.id),
1
);
return next;
});
setLogLines((line) => [`Group Removed ${event.id}`, ...line]);
});
event.api.onDidActiveGroupChange((event) => {
setActiveGroup(event?.id);
setLogLines((line) => [`Group Activated ${event?.id}`, ...line]);
});
const state = localStorage.getItem('dv-demo-state');
if (state) {
try {
event.api.fromJSON(JSON.parse(state));
return;
} catch {
localStorage.removeItem('dv-demo-state');
}
return;
}
defaultConfig(event.api);
};
return (
<div
style={{
height: '100%',
display: 'flex',
flexDirection: 'column',
flexGrow: 1,
}}
>
<div>
<GridActions api={api} />
<PanelActions
api={api}
panels={panels}
activePanel={activePanel}
/>
<GroupActions
api={api}
groups={groups}
activeGroup={activeGroup}
/>
</div>
<div
style={{
flexGrow: 1,
overflow: 'hidden',
// flexBasis: 0
height: 0,
}}
>
<DockviewReact
components={components}
defaultTabComponent={headerComponents.default}
rightHeaderActionsComponent={RightControls}
leftHeaderActionsComponent={LeftControls}
prefixHeaderActionsComponent={PrefixHeaderControls}
onReady={onReady}
className={props.theme || 'dockview-theme-abyss'}
/>
</div>
<div
style={{
height: '200px',
backgroundColor: 'black',
color: 'white',
overflow: 'auto',
}}
>
{logLines.map((line, i) => {
return (
<div key={i}>
<span
style={{
display: 'inline-block',
width: '30px',
color: 'gray',
borderRight: '1px solid gray',
marginRight: '4px',
}}
>
{logLines.length - i}
</span>
<span>{line}</span>
</div>
);
})}
</div>
</div>
);
};
export default DockviewDemo;

View File

@ -0,0 +1,148 @@
import { IDockviewHeaderActionsProps } from 'dockview';
import * as React from 'react';
import { nextId } from './defaultLayout';
const Icon = (props: {
icon: string;
title?: string;
onClick?: (event: React.MouseEvent) => void;
}) => {
return (
<div title={props.title} className="action" onClick={props.onClick}>
<span
style={{ fontSize: 'inherit' }}
className="material-symbols-outlined"
>
{props.icon}
</span>
</div>
);
};
const groupControlsComponents: Record<string, React.FC> = {
panel_1: () => {
return <Icon icon="file_download" />;
},
};
export const RightControls = (props: IDockviewHeaderActionsProps) => {
const Component = React.useMemo(() => {
if (!props.isGroupActive || !props.activePanel) {
return null;
}
return groupControlsComponents[props.activePanel.id];
}, [props.isGroupActive, props.activePanel]);
const [isMaximized, setIsMaximized] = React.useState<boolean>(
props.containerApi.hasMaximizedGroup()
);
const [isPopout, setIsPopout] = React.useState<boolean>(
props.api.location.type === 'popout'
);
React.useEffect(() => {
const disposable = props.containerApi.onDidMaximizedGroupChange(() => {
setIsMaximized(props.containerApi.hasMaximizedGroup());
});
const disposable2 = props.api.onDidLocationChange(() => {
setIsPopout(props.api.location.type === 'popout');
});
return () => {
disposable.dispose();
disposable2.dispose();
};
}, [props.containerApi]);
const onClick = () => {
if (props.containerApi.hasMaximizedGroup()) {
props.containerApi.exitMaximizedGroup();
} else {
props.activePanel?.api.maximize();
}
};
const onClick2 = () => {
if (props.api.location.type !== 'popout') {
props.containerApi.addPopoutGroup(props.group);
} else {
props.api.moveTo({ position: 'right' });
}
};
return (
<div
className="group-control"
style={{
display: 'flex',
alignItems: 'center',
padding: '0px 8px',
height: '100%',
color: 'var(--dv-activegroup-visiblepanel-tab-color)',
}}
>
{props.isGroupActive && <Icon icon="star" />}
{Component && <Component />}
<Icon
title={isPopout ? 'Close Window' : 'Open In New Window'}
icon={isPopout ? 'close_fullscreen' : 'open_in_new'}
onClick={onClick2}
/>
{!isPopout && (
<Icon
title={isMaximized ? 'Minimize View' : 'Maximize View'}
icon={isMaximized ? 'collapse_content' : 'expand_content'}
onClick={onClick}
/>
)}
</div>
);
};
export const LeftControls = (props: IDockviewHeaderActionsProps) => {
const onClick = () => {
props.containerApi.addPanel({
id: `id_${Date.now().toString()}`,
component: 'default',
title: `Tab ${nextId()}`,
position: {
referenceGroup: props.group,
},
});
};
return (
<div
className="group-control"
style={{
display: 'flex',
alignItems: 'center',
padding: '0px 8px',
height: '100%',
color: 'var(--dv-activegroup-visiblepanel-tab-color)',
}}
>
<Icon onClick={onClick} icon="add" />
</div>
);
};
export const PrefixHeaderControls = (props: IDockviewHeaderActionsProps) => {
return (
<div
className="group-control"
style={{
display: 'flex',
alignItems: 'center',
padding: '0px 8px',
height: '100%',
color: 'var(--dv-activegroup-visiblepanel-tab-color)',
}}
>
<Icon icon="Menu" />
</div>
);
};

View File

@ -0,0 +1,179 @@
import {
DockviewGroupLocation,
DockviewPanelApi,
DockviewPanelRenderer,
} from 'dockview';
import * as React from 'react';
export interface PanelApiMetadata {
isActive: {
value: boolean;
count: number;
};
isVisible: {
value: boolean;
count: number;
};
isHidden: {
value: boolean;
count: number;
};
renderer: {
value: DockviewPanelRenderer;
count: number;
};
isGroupActive: {
value: boolean;
count: number;
};
groupChanged: {
count: number;
};
location: {
value: DockviewGroupLocation;
count: number;
};
didFocus: {
count: number;
};
dimensions: {
count: number;
value: { height: number; width: number };
};
}
export const Table = (props: { data: PanelApiMetadata }) => {
return (
<div className="data-table">
<table>
<tr>
<th>{'Key'}</th>
<th>{'Count'}</th>
<th>{'Value'}</th>
</tr>
{Object.entries(props.data).map(([key, value]) => {
return (
<tr key={key}>
<th>{key}</th>
<th>{value.count}</th>
<th>{JSON.stringify(value.value, null, 4)}</th>
</tr>
);
})}
</table>
</div>
);
};
export function usePanelApiMetadata(api: DockviewPanelApi): PanelApiMetadata {
const [state, setState] = React.useState<PanelApiMetadata>({
isActive: { value: api.isActive, count: 0 },
isVisible: { value: api.isVisible, count: 0 },
isHidden: { value: api.isHidden, count: 0 },
renderer: { value: api.renderer, count: 0 },
isGroupActive: { value: api.isGroupActive, count: 0 },
groupChanged: { count: 0 },
location: { value: api.location, count: 0 },
didFocus: { count: 0 },
dimensions: {
count: 0,
value: { height: api.height, width: api.width },
},
});
React.useEffect(() => {
const d1 = api.onDidActiveChange((event) => {
setState((_) => ({
..._,
isActive: {
value: event.isActive,
count: _.isActive.count + 1,
},
}));
});
const d2 = api.onDidActiveGroupChange((event) => {
setState((_) => ({
..._,
isGroupActive: {
value: event.isActive,
count: _.isGroupActive.count + 1,
},
}));
});
const d3 = api.onDidDimensionsChange((event) => {
setState((_) => ({
..._,
dimensions: {
count: _.dimensions.count + 1,
value: { height: event.height, width: event.width },
},
}));
});
const d4 = api.onDidFocusChange((event) => {
setState((_) => ({
..._,
didFocus: {
count: _.didFocus.count + 1,
},
}));
});
const d5 = api.onDidGroupChange((event) => {
setState((_) => ({
..._,
groupChanged: {
count: _.groupChanged.count + 1,
},
}));
});
const d6 = api.onDidHiddenChange((event) => {
setState((_) => ({
..._,
isHidden: {
value: event.isHidden,
count: _.isHidden.count + 1,
},
}));
});
const d7 = api.onDidLocationChange((event) => {
setState((_) => ({
..._,
location: {
value: event.location,
count: _.location.count + 1,
},
}));
});
const d8 = api.onDidRendererChange((event) => {
setState((_) => ({
..._,
renderer: {
value: event.renderer,
count: _.renderer.count + 1,
},
}));
});
const d9 = api.onDidVisibilityChange((event) => {
setState((_) => ({
..._,
isVisible: {
value: event.isVisible,
count: _.isVisible.count + 1,
},
}));
});
return () => {
d1.dispose();
d2.dispose();
d3.dispose();
d4.dispose();
d5.dispose();
d6.dispose();
d7.dispose();
d8.dispose();
d9.dispose();
};
}, [api]);
return state;
}

View File

@ -0,0 +1,69 @@
import { DockviewApi } from 'dockview';
export const nextId = (() => {
let counter = 0;
return () => {
counter++;
};
})();
export function defaultConfig(api: DockviewApi) {
const panel1 = api.addPanel({
id: 'panel_1',
component: 'iframe',
renderer: 'always',
title: 'Panel 1',
});
api.addPanel({
id: 'panel_2',
component: 'default',
title: 'Panel 2',
position: { referencePanel: panel1 },
});
api.addPanel({
id: 'panel_3',
component: 'default',
title: 'Panel 3',
position: { referencePanel: panel1 },
});
const panel4 = api.addPanel({
id: 'panel_4',
component: 'default',
title: 'Panel 4',
position: { referencePanel: panel1, direction: 'right' },
});
const panel5 = api.addPanel({
id: 'panel_5',
component: 'default',
title: 'Panel 5',
position: { referencePanel: panel4 },
});
const panel6 = api.addPanel({
id: 'panel_6',
component: 'default',
title: 'Panel 6',
position: { referencePanel: panel5, direction: 'below' },
});
const panel7 = api.addPanel({
id: 'panel_7',
component: 'default',
title: 'Panel 7',
position: { referencePanel: panel6, direction: 'left' },
});
api.addPanel({
id: 'panel8',
component: 'default',
title: 'Panel 8',
position: { referencePanel: panel7, direction: 'below' },
});
panel1.api.setActive();
}

View File

@ -0,0 +1,71 @@
import { DockviewApi } from 'dockview';
import * as React from 'react';
import { defaultConfig, nextId } from './defaultLayout';
export const GridActions = (props: { api?: DockviewApi }) => {
const onClear = () => {
props.api?.clear();
};
const onLoad = () => {
const state = localStorage.getItem('dv-demo-state');
if (state) {
try {
props.api?.fromJSON(JSON.parse(state));
} catch {
localStorage.removeItem('dv-demo-state');
}
}
};
const onSave = () => {
if (props.api) {
localStorage.setItem(
'dv-demo-state',
JSON.stringify(props.api.toJSON())
);
}
};
const onReset = () => {
if (props.api) {
props.api.clear();
defaultConfig(props.api);
}
};
const onAddPanel = () => {
props.api?.addPanel({
id: `id_${Date.now().toString()}`,
component: 'default',
title: `Tab ${nextId()}`,
});
};
const onAddGroup = () => {
props.api?.addGroup();
};
return (
<div className="action-container">
<button className="text-button" onClick={onAddPanel}>
Add Panel
</button>
<button className="text-button" onClick={onAddGroup}>
Add Group
</button>
<button className="text-button" onClick={onClear}>
Clear
</button>
<button className="text-button" onClick={onLoad}>
Load
</button>
<button className="text-button" onClick={onSave}>
Save
</button>
<button className="text-button" onClick={onReset}>
Reset
</button>
</div>
);
};

View File

@ -0,0 +1,87 @@
import { DockviewApi } from 'dockview';
export const GroupActions = (props: {
groups: string[];
api?: DockviewApi;
activeGroup?: string;
}) => {
return (
<div className="action-container">
{props.groups.map((x) => {
const onClick = () => {
props.api?.getGroup(x)?.focus();
};
return (
<div className="button-action">
<div style={{ display: 'flex' }}>
<button
onClick={onClick}
className={
props.activeGroup === x
? 'demo-button selected'
: 'demo-button'
}
>
{x}
</button>
</div>
<div style={{ display: 'flex' }}>
<button
className="demo-icon-button"
onClick={() => {
const panel = props.api?.getGroup(x);
if (panel) {
props.api?.addFloatingGroup(panel);
}
}}
>
<span className="material-symbols-outlined">
ad_group
</span>
</button>
<button
className="demo-icon-button"
onClick={() => {
const panel = props.api?.getGroup(x);
if (panel) {
props.api?.addPopoutGroup(panel);
}
}}
>
<span className="material-symbols-outlined">
open_in_new
</span>
</button>
<button
className="demo-icon-button"
onClick={() => {
const panel = props.api?.getGroup(x);
if (panel?.api.isMaximized()) {
panel.api.exitMaximized();
} else {
panel?.api.maximize();
}
}}
>
<span className="material-symbols-outlined">
fullscreen
</span>
</button>
<button
className="demo-icon-button"
onClick={() => {
const panel = props.api?.getGroup(x);
panel?.api.close();
}}
>
<span className="material-symbols-outlined">
close
</span>
</button>
</div>
</div>
);
})}
</div>
);
};

View File

@ -0,0 +1,72 @@
import { DockviewApi } from 'dockview';
export const PanelActions = (props: {
panels: string[];
api?: DockviewApi;
activePanel?: string;
}) => {
return (
<div className="action-container">
{props.panels.map((x) => {
const onClick = () => {
props.api?.getPanel(x)?.focus();
};
return (
<div className="button-action">
<div style={{ display: 'flex' }}>
<button
className={
props.activePanel === x
? 'demo-button selected'
: 'demo-button'
}
onClick={onClick}
>
{x}
</button>
</div>
<div style={{ display: 'flex' }}>
<button
className="demo-icon-button"
onClick={() => {
const panel = props.api?.getPanel(x);
if (panel) {
props.api?.addFloatingGroup(panel);
}
}}
>
<span className="material-symbols-outlined">
ad_group
</span>
</button>
<button
className="demo-icon-button"
onClick={() => {
const panel = props.api?.getPanel(x);
if (panel) {
props.api?.addPopoutGroup(panel);
}
}}
>
<span className="material-symbols-outlined">
open_in_new
</span>
</button>
<button
className="demo-icon-button"
onClick={() => {
const panel = props.api?.getPanel(x);
panel?.api.close();
}}
>
<span className="material-symbols-outlined">
close
</span>
</button>
</div>
</div>
);
})}
</div>
);
};

View File

@ -1,5 +1,5 @@
{
"name": "updatetitle-dockview",
"name": "dockview.dnd-events",
"description": "",
"keywords": [
"dockview"
@ -29,4 +29,4 @@
"not ie <= 11",
"not op_mini all"
]
}
}

View File

@ -0,0 +1,139 @@
import {
DockviewApi,
DockviewReact,
DockviewReadyEvent,
IDockviewPanelProps,
} from 'dockview';
import * as React from 'react';
const Default = (props: IDockviewPanelProps) => {
return (
<div style={{ height: '100%' }}>
<div>{props.api.title}</div>
</div>
);
};
const components = {
default: Default,
};
const Component = (props: { theme?: string }) => {
const [disablePanelDrag, setDisablePanelDrag] =
React.useState<boolean>(false);
const [disableGroupDrag, setDisableGroupDrag] =
React.useState<boolean>(false);
const [disableOverlay, setDisableOverlay] = React.useState<boolean>(false);
const [api, setApi] = React.useState<DockviewApi>();
React.useEffect(() => {
if (!api) {
return;
}
const disposables = [
api.onWillDragPanel((e) => {
if (!disablePanelDrag) {
return;
}
e.nativeEvent.preventDefault();
}),
api.onWillDragGroup((e) => {
if (!disableGroupDrag) {
return;
}
e.nativeEvent.preventDefault();
}),
api.onWillShowOverlay((e) => {
if (!disableOverlay) {
return;
}
e.preventDefault();
}),
api.onWillDrop((e) => {
//
}),
api.onDidDrop((e) => {
//
}),
];
return () => {
disposables.forEach((disposable) => {
disposable.dispose();
});
};
}, [api, disablePanelDrag, disableGroupDrag]);
const onReady = (event: DockviewReadyEvent) => {
setApi(event.api);
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',
});
};
return (
<div
style={{ display: 'flex', flexDirection: 'column', height: '100%' }}
>
<div>
<button
onClick={() => setDisablePanelDrag(!disablePanelDrag)}
>{`Panel Drag: ${
disablePanelDrag ? 'disabled' : 'enabled'
}`}</button>
<button
onClick={() => setDisableGroupDrag(!disableGroupDrag)}
>{`Group Drag: ${
disableGroupDrag ? 'disabled' : 'enabled'
}`}</button>
<button
onClick={() => setDisableOverlay(!disableOverlay)}
>{`Overlay: ${
disableOverlay ? 'disabled' : 'enabled'
}`}</button>
</div>
<div style={{ flexGrow: 1 }}>
<DockviewReact
className={`${props.theme || 'dockview-theme-abyss'}`}
onReady={onReady}
components={components}
/>
</div>
</div>
);
};
export default Component;

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