From 36299f8c93ed2f5081180737d8a36caeda265924 Mon Sep 17 00:00:00 2001 From: mathuo <6710312+mathuo@users.noreply.github.com> Date: Mon, 3 Apr 2023 21:53:40 +0100 Subject: [PATCH 01/22] feat: tsconfig adjustments --- module-build/tsconfig.esm.json | 25 ------------------ packages/dockview-core/rollup.config.js | 1 + packages/dockview-core/tsconfig.esm.json | 5 +++- packages/dockview-core/tsconfig.json | 4 +-- packages/dockview/rollup.config.js | 1 + packages/dockview/tsconfig.esm.json | 5 +++- packages/dockview/tsconfig.json | 4 +-- .../tsconfig.json => tsconfig.base.json | 26 +++++++++---------- tsconfig.eslint.json | 14 +++++----- 9 files changed, 34 insertions(+), 51 deletions(-) delete mode 100644 module-build/tsconfig.esm.json rename module-build/tsconfig.json => tsconfig.base.json (73%) diff --git a/module-build/tsconfig.esm.json b/module-build/tsconfig.esm.json deleted file mode 100644 index 1520dd78c..000000000 --- a/module-build/tsconfig.esm.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "compilerOptions": { - "module": "ES2020", - "declaration": true, - "target": "es6", - "moduleResolution": "node", - "esModuleInterop": true, - "downlevelIteration": true, - "incremental": true, - "noImplicitReturns": true, - "noImplicitAny": true, - "allowUnreachableCode": false, - "forceConsistentCasingInFileNames": true, - "strict": true, - "declarationMap": true, - "lib": [ - "ES2015", - "ES2016.Array.Include", - "ES2017.String", - "ES2018.Promise", - "ES2019", - "DOM", - ] - } -} diff --git a/packages/dockview-core/rollup.config.js b/packages/dockview-core/rollup.config.js index ad925e4ff..7c8a12d45 100644 --- a/packages/dockview-core/rollup.config.js +++ b/packages/dockview-core/rollup.config.js @@ -55,6 +55,7 @@ function createBundle(format, options) { ` * @license ${license}`, ` */`, ].join('\n'), + sourcemap: true, }; const plugins = [ diff --git a/packages/dockview-core/tsconfig.esm.json b/packages/dockview-core/tsconfig.esm.json index b48538b62..156f7d124 100644 --- a/packages/dockview-core/tsconfig.esm.json +++ b/packages/dockview-core/tsconfig.esm.json @@ -1,6 +1,9 @@ { - "extends": "../../module-build/tsconfig.esm.json", + "extends": "../../tsconfig.base.json", "compilerOptions": { + "module": "ES2020", + "moduleResolution": "node", + "target": "es6", "outDir": "dist/esm", "tsBuildInfoFile": ".build/tsconfig.tsbuildinfo.esm", "jsx": "react", diff --git a/packages/dockview-core/tsconfig.json b/packages/dockview-core/tsconfig.json index 9ff78cef1..023097d12 100644 --- a/packages/dockview-core/tsconfig.json +++ b/packages/dockview-core/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "../../module-build/tsconfig.json", + "extends": "../../tsconfig.base.json", "compilerOptions": { "outDir": "dist/cjs", "tsBuildInfoFile": ".build/tsconfig.tsbuildinfo.cjs", @@ -8,4 +8,4 @@ }, "include": ["src"], "exclude": ["**/node_modules", "src/__tests__"] -} \ No newline at end of file +} diff --git a/packages/dockview/rollup.config.js b/packages/dockview/rollup.config.js index ba0fb90fd..b522bd0c0 100644 --- a/packages/dockview/rollup.config.js +++ b/packages/dockview/rollup.config.js @@ -56,6 +56,7 @@ function createBundle(format, options) { ` * @license ${license}`, ` */`, ].join('\n'), + sourcemap: true, }; const plugins = [ diff --git a/packages/dockview/tsconfig.esm.json b/packages/dockview/tsconfig.esm.json index b48538b62..156f7d124 100644 --- a/packages/dockview/tsconfig.esm.json +++ b/packages/dockview/tsconfig.esm.json @@ -1,6 +1,9 @@ { - "extends": "../../module-build/tsconfig.esm.json", + "extends": "../../tsconfig.base.json", "compilerOptions": { + "module": "ES2020", + "moduleResolution": "node", + "target": "es6", "outDir": "dist/esm", "tsBuildInfoFile": ".build/tsconfig.tsbuildinfo.esm", "jsx": "react", diff --git a/packages/dockview/tsconfig.json b/packages/dockview/tsconfig.json index 932770600..f1a6f570c 100644 --- a/packages/dockview/tsconfig.json +++ b/packages/dockview/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "../../module-build/tsconfig.json", + "extends": "../../tsconfig.base.json", "compilerOptions": { "outDir": "dist/cjs", "tsBuildInfoFile": ".build/tsconfig.tsbuildinfo.cjs", @@ -7,7 +7,7 @@ "rootDir": "src" }, "paths": { - "dockview-core": "../dockview-core" + "dockview-core": "../dockview-core" }, "include": ["src"], "exclude": ["**/node_modules", "src/__tests__"] diff --git a/module-build/tsconfig.json b/tsconfig.base.json similarity index 73% rename from module-build/tsconfig.json rename to tsconfig.base.json index e89bd96f9..77f0e8b99 100644 --- a/module-build/tsconfig.json +++ b/tsconfig.base.json @@ -1,25 +1,25 @@ { "compilerOptions": { "module": "commonjs", - "declaration": true, "target": "es5", + "declaration": true, + "declarationMap": true, + "sourceMap": true, + "strict": true, + "allowUnreachableCode": false, + "forceConsistentCasingInFileNames": true, "esModuleInterop": true, "downlevelIteration": true, "incremental": true, - "sourceMap": true, "noImplicitReturns": true, "noImplicitAny": true, - "allowUnreachableCode": false, - "forceConsistentCasingInFileNames": true, - "strict": true, - "declarationMap": true, "lib": [ - "ES2015", - "ES2016.Array.Include", - "ES2017.String", - "ES2018.Promise", - "ES2019", - "DOM", - ] + "ES2015", + "ES2016.Array.Include", + "ES2017.String", + "ES2018.Promise", + "ES2019", + "DOM" + ] } } diff --git a/tsconfig.eslint.json b/tsconfig.eslint.json index 4343d501d..0703d75a7 100644 --- a/tsconfig.eslint.json +++ b/tsconfig.eslint.json @@ -1,9 +1,9 @@ { - "compilerOptions": { - "types": ["@types/node"], - "noEmit": true, - "allowJs": true - }, - "extends": "./module-build/tsconfig.esm.json", - "include": ["tests/**/*.ts", "tools/**/*.ts", ".eslintrc.js"] + "compilerOptions": { + "types": ["@types/node"], + "noEmit": true, + "allowJs": true + }, + "extends": "./tsconfig.base.json", + "include": ["tests/**/*.ts", "tools/**/*.ts", ".eslintrc.js"] } From e07cedf01f4e5e96be7fa5a74dd3cca0f8fbd264 Mon Sep 17 00:00:00 2001 From: mathuo <6710312+mathuo@users.noreply.github.com> Date: Sat, 1 Apr 2023 21:08:32 +0100 Subject: [PATCH 02/22] feat: remove tabHeight setter --- .../dockview/dockviewComponent.spec.ts | 13 --------- .../groupview/dockviewGroupPanelModel.spec.ts | 6 ++-- .../dockview-core/src/api/component.api.ts | 8 ----- .../components/titlebar/tabsContainer.ts | 22 -------------- .../src/dockview/dockviewComponent.ts | 29 ++----------------- .../src/dockview/dockviewGroupPanelModel.ts | 2 -- .../src/dockview/dockviewPanel.ts | 9 +++--- .../dockview-core/src/dockview/options.ts | 1 - .../dockview-core/src/gridview/options.ts | 1 - packages/dockview/src/dockview/dockview.tsx | 2 -- packages/docs/docs/components/dockview.mdx | 3 -- .../version-1.7.0/components/dockview.mdx | 7 ++--- 12 files changed, 10 insertions(+), 93 deletions(-) diff --git a/packages/dockview-core/src/__tests__/dockview/dockviewComponent.spec.ts b/packages/dockview-core/src/__tests__/dockview/dockviewComponent.spec.ts index 25206344b..8ce3e9d32 100644 --- a/packages/dockview-core/src/__tests__/dockview/dockviewComponent.spec.ts +++ b/packages/dockview-core/src/__tests__/dockview/dockviewComponent.spec.ts @@ -473,7 +473,6 @@ describe('dockviewComponent', () => { title: 'panel5', }, }, - options: { tabHeight: 25 }, }); expect(JSON.parse(JSON.stringify(dockview.toJSON()))).toEqual({ @@ -558,7 +557,6 @@ describe('dockviewComponent', () => { title: 'panel5', }, }, - options: { tabHeight: 25 }, }); }); @@ -989,7 +987,6 @@ describe('dockviewComponent', () => { title: 'view_3_title', }, }, - options: {}, }); expect(removedGroups.length).toBe(2); @@ -1512,7 +1509,6 @@ describe('dockviewComponent', () => { title: 'panel5', }, }, - options: { tabHeight: 25 }, }); jest.runAllTimers(); @@ -1547,7 +1543,6 @@ describe('dockviewComponent', () => { orientation: Orientation.VERTICAL, }, panels: {}, - options: { tabHeight: 25 }, }); jest.runAllTimers(); @@ -1647,7 +1642,6 @@ describe('dockviewComponent', () => { title: 'panel5', }, }, - options: { tabHeight: 25 }, }); }); @@ -1709,7 +1703,6 @@ describe('dockviewComponent', () => { title: 'panel2', }, }, - options: { tabHeight: 25 }, }); expect(JSON.parse(JSON.stringify(dockview.toJSON()))).toEqual({ @@ -1756,7 +1749,6 @@ describe('dockviewComponent', () => { title: 'panel2', }, }, - options: { tabHeight: 25 }, }); }); @@ -1822,7 +1814,6 @@ describe('dockviewComponent', () => { title: 'panel3', }, }, - options: { tabHeight: 25 }, }); const group = dockview.getGroupPanel('panel2')!.api.group; @@ -1949,7 +1940,6 @@ describe('dockviewComponent', () => { title: 'panel2', }, }, - options: {}, }); }); @@ -2090,7 +2080,6 @@ describe('dockviewComponent', () => { title: 'panel3', }, }, - options: {}, }); }); @@ -2218,7 +2207,6 @@ describe('dockviewComponent', () => { title: 'panel3', }, }, - options: {}, }); }); @@ -2304,7 +2292,6 @@ describe('dockviewComponent', () => { size: 0, }, }, - options: {}, panels: {}, }); }); diff --git a/packages/dockview-core/src/__tests__/groupview/dockviewGroupPanelModel.spec.ts b/packages/dockview-core/src/__tests__/groupview/dockviewGroupPanelModel.spec.ts index 4bbc0237f..ef2ed7cad 100644 --- a/packages/dockview-core/src/__tests__/groupview/dockviewGroupPanelModel.spec.ts +++ b/packages/dockview-core/src/__tests__/groupview/dockviewGroupPanelModel.spec.ts @@ -237,6 +237,8 @@ describe('groupview', () => { removePanelMock = jest.fn(); removeGroupMock = jest.fn(); + options = {}; + dockview = (>{ options: {}, createWatermarkComponent: () => new Watermark(), @@ -248,9 +250,6 @@ describe('groupview', () => { onDidRemovePanel: jest.fn(), }) as DockviewComponent; - options = { - tabHeight: 30, - }; groupview = new DockviewGroupPanel(dockview, 'groupview-1', options); groupview.initialize(); }); @@ -261,7 +260,6 @@ describe('groupview', () => { const panel3 = new TestPanel('panel3', jest.fn() as any); const groupview2 = new DockviewGroupPanel(dockview, 'groupview-2', { - tabHeight: 25, panels: [panel1, panel2, panel3], activePanel: panel2, }); diff --git a/packages/dockview-core/src/api/component.api.ts b/packages/dockview-core/src/api/component.api.ts index 4de792690..d527ef6af 100644 --- a/packages/dockview-core/src/api/component.api.ts +++ b/packages/dockview-core/src/api/component.api.ts @@ -419,14 +419,6 @@ export class DockviewApi implements CommonApi { constructor(private readonly component: IDockviewComponent) {} - getTabHeight(): number | undefined { - return this.component.tabHeight; - } - - setTabHeight(height: number | undefined): void { - this.component.tabHeight = height; - } - focus(): void { this.component.focus(); } diff --git a/packages/dockview-core/src/dockview/components/titlebar/tabsContainer.ts b/packages/dockview-core/src/dockview/components/titlebar/tabsContainer.ts index 2472945a4..debdf42e0 100644 --- a/packages/dockview-core/src/dockview/components/titlebar/tabsContainer.ts +++ b/packages/dockview-core/src/dockview/components/titlebar/tabsContainer.ts @@ -20,7 +20,6 @@ export interface ITabsContainer extends IDisposable { readonly element: HTMLElement; readonly panels: string[]; readonly size: number; - height: number | undefined; delete: (id: string) => void; indexOf: (id: string) => number; onDrop: Event; @@ -48,7 +47,6 @@ export class TabsContainer private selectedIndex = -1; private actions: HTMLElement | undefined; - private _height: number | undefined; private _hidden = false; private readonly _onDrop = new Emitter(); @@ -62,24 +60,6 @@ export class TabsContainer return this.tabs.length; } - get height(): number | undefined { - return this._height; - } - - set height(value: number | undefined) { - this._height = value; - if (typeof value !== 'number') { - this.element.style.removeProperty( - '--dv-tabs-and-actions-container-height' - ); - } else { - this.element.style.setProperty( - '--dv-tabs-and-actions-container-height', - `${value}px` - ); - } - } - get hidden(): boolean { return this._hidden; } @@ -139,8 +119,6 @@ export class TabsContainer this._element = document.createElement('div'); this._element.className = 'tabs-and-actions-container'; - this.height = accessor.options.tabHeight; - toggleClass( this._element, 'dv-full-width-single-tab', diff --git a/packages/dockview-core/src/dockview/dockviewComponent.ts b/packages/dockview-core/src/dockview/dockviewComponent.ts index 13eb455aa..0cb6ee8e1 100644 --- a/packages/dockview-core/src/dockview/dockviewComponent.ts +++ b/packages/dockview-core/src/dockview/dockviewComponent.ts @@ -59,7 +59,6 @@ export interface SerializedDockview { }; panels: { [key: string]: GroupviewPanelState }; activeGroup?: string; - options?: { tabHeight?: number }; } export type DockviewComponentUpdateOptions = Pick< @@ -86,7 +85,6 @@ export interface IDockviewComponent extends IBaseGrid { readonly panels: IDockviewPanel[]; readonly onDidDrop: Event; readonly orientation: Orientation; - tabHeight: number | undefined; updateOptions(options: DockviewComponentUpdateOptions): void; moveGroupOrPanel( referenceGroup: DockviewGroupPanel, @@ -174,17 +172,6 @@ export class DockviewComponent return activeGroup.activePanel; } - set tabHeight(height: number | undefined) { - this.options.tabHeight = height; - this._groups.forEach((value) => { - value.value.model.header.height = height; - }); - } - - get tabHeight(): number | undefined { - return this.options.tabHeight; - } - constructor(options: DockviewComponentOptions) { super({ proportionalLayout: true, @@ -413,18 +400,13 @@ export class DockviewComponent grid: data, panels, activeGroup: this.activeGroup?.id, - options: { tabHeight: this.tabHeight }, }; } fromJSON(data: SerializedDockview): void { this.clear(); - const { grid, panels, options, activeGroup } = data; - - if (typeof options?.tabHeight === 'number') { - this.tabHeight = options.tabHeight; - } + const { grid, panels, activeGroup } = data; if (grid.root.type !== 'branch' || !Array.isArray(grid.root.data)) { throw new Error('root must be of type branch'); @@ -869,10 +851,7 @@ export class DockviewComponent createGroup(options?: GroupOptions): DockviewGroupPanel { if (!options) { - options = { tabHeight: this.tabHeight }; - } - if (typeof options.tabHeight !== 'number') { - options.tabHeight = this.tabHeight; + options = {}; } let id = options?.id; @@ -925,10 +904,6 @@ export class DockviewComponent // not an ideal pattern view.initialize(); - if (typeof this.options.tabHeight === 'number') { - view.model.header.height = this.options.tabHeight; - } - return view; } diff --git a/packages/dockview-core/src/dockview/dockviewGroupPanelModel.ts b/packages/dockview-core/src/dockview/dockviewGroupPanelModel.ts index 37836691d..5e9773a73 100644 --- a/packages/dockview-core/src/dockview/dockviewGroupPanelModel.ts +++ b/packages/dockview-core/src/dockview/dockviewGroupPanelModel.ts @@ -56,7 +56,6 @@ export interface GroupOptions extends CoreGroupOptions { readonly panels?: IDockviewPanel[]; readonly activePanel?: IDockviewPanel; readonly id?: string; - tabHeight?: number; } export interface GroupPanelViewState extends CoreGroupOptions { @@ -78,7 +77,6 @@ export interface GroupviewDropEvent { export interface IHeader { hidden: boolean; - height: number | undefined; } export interface IDockviewGroupPanelModel extends IPanel { diff --git a/packages/dockview-core/src/dockview/dockviewPanel.ts b/packages/dockview-core/src/dockview/dockviewPanel.ts index 36c621a16..66ac5be5f 100644 --- a/packages/dockview-core/src/dockview/dockviewPanel.ts +++ b/packages/dockview-core/src/dockview/dockviewPanel.ts @@ -78,7 +78,7 @@ export class DockviewPanel this.setTitle(params.title); - this.view?.init({ + this.view.init({ ...params, api: this.api, containerApi: this.containerApi, @@ -165,15 +165,14 @@ export class DockviewPanel // the obtain the correct dimensions of the content panel we must deduct the tab height this.api._onDidDimensionChange.fire({ width, - height: height - (this.group.model.header.height || 0), + height: height, }); - this.view?.layout(width, height); + this.view.layout(width, height); } public dispose(): void { this.api.dispose(); - - this.view?.dispose(); + this.view.dispose(); } } diff --git a/packages/dockview-core/src/dockview/options.ts b/packages/dockview-core/src/dockview/options.ts index f1751c7fb..1e9b66db1 100644 --- a/packages/dockview-core/src/dockview/options.ts +++ b/packages/dockview-core/src/dockview/options.ts @@ -75,7 +75,6 @@ export interface DockviewComponentOptions extends DockviewRenderFunctions { watermarkComponent?: WatermarkConstructor; watermarkFrameworkComponent?: any; frameworkComponentFactory?: GroupPanelFrameworkComponentFactory; - tabHeight?: number; orientation?: Orientation; styles?: ISplitviewStyles; defaultTabComponent?: string; diff --git a/packages/dockview-core/src/gridview/options.ts b/packages/dockview-core/src/gridview/options.ts index 7596bb0b2..461ab6c66 100644 --- a/packages/dockview-core/src/gridview/options.ts +++ b/packages/dockview-core/src/gridview/options.ts @@ -14,7 +14,6 @@ export interface GridviewComponentOptions { [componentName: string]: any; }; frameworkComponentFactory?: FrameworkFactory; - tabHeight?: number; styles?: ISplitviewStyles; parentElement?: HTMLElement; } diff --git a/packages/dockview/src/dockview/dockview.tsx b/packages/dockview/src/dockview/dockview.tsx index f9d97ba03..6426bcece 100644 --- a/packages/dockview/src/dockview/dockview.tsx +++ b/packages/dockview/src/dockview/dockview.tsx @@ -58,7 +58,6 @@ export interface IDockviewReactProps { components: PanelCollection; tabComponents?: PanelCollection; watermarkComponent?: React.FunctionComponent; - tabHeight?: number; onDidDrop?: (event: DockviewDropEvent) => void; showDndOverlay?: (event: DockviewDndOverlayEvent) => boolean; hideBorders?: boolean; @@ -142,7 +141,6 @@ export const DockviewReact = React.forwardRef( frameworkComponentFactory: factory, frameworkComponents: props.components, frameworkTabComponents, - tabHeight: props.tabHeight, watermarkFrameworkComponent: props.watermarkComponent, defaultTabComponent: props.defaultTabComponent ? DEFAULT_REACT_TAB diff --git a/packages/docs/docs/components/dockview.mdx b/packages/docs/docs/components/dockview.mdx index 61fcf4e2f..3e9910d0e 100644 --- a/packages/docs/docs/components/dockview.mdx +++ b/packages/docs/docs/components/dockview.mdx @@ -59,7 +59,6 @@ import { DockviewReact } from 'dockview'; | showDndOverlay | Event | Yes | false | | | defaultTabComponent | object | Yes | | | | groupControlComponent | object | Yes | | | -| tabHeight | number | Yes | | | | singleTabMode | 'fullwidth' \| 'default' | Yes | 'default' | | ## Dockview API @@ -113,8 +112,6 @@ const onReady = (event: DockviewReadyEvent) => { | removeGroup | `(group: GroupPanel): void` | | | getGroup | `(id: string): GroupPanel \| undefined` | | | | | | -| getTabHeight | `(): number \| undefined` | | -| setTabHeight | `(height: number \| undefined): void` | | | updateOptions | `(options:SplitviewComponentUpdateOptions): void` | | | focus | `(): void` | | | layout | `(width: number, height:number): void` | Auto Resizing | diff --git a/packages/docs/versioned_docs/version-1.7.0/components/dockview.mdx b/packages/docs/versioned_docs/version-1.7.0/components/dockview.mdx index 61fcf4e2f..f48ade881 100644 --- a/packages/docs/versioned_docs/version-1.7.0/components/dockview.mdx +++ b/packages/docs/versioned_docs/version-1.7.0/components/dockview.mdx @@ -47,7 +47,7 @@ import { DockviewReact } from 'dockview'; ``` | Property | Type | Optional | Default | Description | -| --------------------- | ------------------------------------ | -------- | --------- | ------------------------------------------------------------ | +| --------------------- | ------------------------------------ | -------- | --------- | ------------------------------------------------------------ | --- | | onReady | (event: SplitviewReadyEvent) => void | No | | | | components | object | No | | | | tabComponents | object | Yes | | | @@ -58,8 +58,7 @@ import { DockviewReact } from 'dockview'; | onDidDrop | Event | Yes | false | | | showDndOverlay | Event | Yes | false | | | defaultTabComponent | object | Yes | | | -| groupControlComponent | object | Yes | | | -| tabHeight | number | Yes | | | +| groupControlComponent | object | Yes | | | | | singleTabMode | 'fullwidth' \| 'default' | Yes | 'default' | | ## Dockview API @@ -113,8 +112,6 @@ const onReady = (event: DockviewReadyEvent) => { | removeGroup | `(group: GroupPanel): void` | | | getGroup | `(id: string): GroupPanel \| undefined` | | | | | | -| getTabHeight | `(): number \| undefined` | | -| setTabHeight | `(height: number \| undefined): void` | | | updateOptions | `(options:SplitviewComponentUpdateOptions): void` | | | focus | `(): void` | | | layout | `(width: number, height:number): void` | Auto Resizing | From 82df78406530e80e7ea55454fc8a41287d80c417 Mon Sep 17 00:00:00 2001 From: mathuo <6710312+mathuo@users.noreply.github.com> Date: Mon, 3 Apr 2023 22:09:19 +0100 Subject: [PATCH 03/22] chore: type definition fix --- packages/dockview/src/dockview/dockview.tsx | 7 +-- .../customheader-dockview/src/app.tsx | 48 ++++++++++++++----- 2 files changed, 40 insertions(+), 15 deletions(-) diff --git a/packages/dockview/src/dockview/dockview.tsx b/packages/dockview/src/dockview/dockview.tsx index f9d97ba03..6a439c080 100644 --- a/packages/dockview/src/dockview/dockview.tsx +++ b/packages/dockview/src/dockview/dockview.tsx @@ -37,14 +37,15 @@ function createGroupControlElement( : undefined; } -export interface IGroupPanelBaseProps> +export interface IGroupPanelBaseProps extends PanelParameters { api: DockviewPanelApi; containerApi: DockviewApi; } -export type IDockviewPanelHeaderProps> = - IGroupPanelBaseProps; +export type IDockviewPanelHeaderProps< + T extends { [index: string]: any } = any +> = IGroupPanelBaseProps; export type IDockviewPanelProps = IGroupPanelBaseProps; diff --git a/packages/docs/sandboxes/customheader-dockview/src/app.tsx b/packages/docs/sandboxes/customheader-dockview/src/app.tsx index eb2ecfffc..fe21c5307 100644 --- a/packages/docs/sandboxes/customheader-dockview/src/app.tsx +++ b/packages/docs/sandboxes/customheader-dockview/src/app.tsx @@ -7,25 +7,25 @@ import { } from 'dockview'; import * as React from 'react'; +interface CustomProps { + valueA: string; +} + const components = { - default: (props: IDockviewPanelProps<{ title: string }>) => { - return
{props.params.title}
; + default: (props: IDockviewPanelProps) => { + return
{props.api.title}
; }, }; -const MyCustomheader = (props: IDockviewPanelHeaderProps) => { - const onContextMenu = (event: React.MouseEvent) => { - event.preventDefault(); - alert('context menu'); - }; - return ; -}; - const headerComponents = { - default: (props: IDockviewPanelHeaderProps) => { + default: (props: IDockviewPanelHeaderProps) => { const onContextMenu = (event: React.MouseEvent) => { event.preventDefault(); - alert('context menu'); + alert( + `This custom header was parsed the params ${JSON.stringify( + props.params + )}` + ); }; return ; }, @@ -39,34 +39,52 @@ const CustomHeadersDockview = () => { id: 'panel_1', component: 'default', title: 'Panel 1', + params: { + valueA: 'test value', + }, }); event.api.addPanel({ id: 'panel_2', component: 'default', title: 'Panel 2', + params: { + valueA: 'test value', + }, }); event.api.addPanel({ id: 'panel_3', component: 'default', title: 'Panel 3', + params: { + valueA: 'test value', + }, }); event.api.addPanel({ id: 'panel_4', component: 'default', title: 'Panel 4', position: { referencePanel: 'panel_3', direction: 'right' }, + params: { + valueA: 'test value', + }, }); event.api.addPanel({ id: 'panel_5', component: 'default', title: 'Panel 5', position: { referencePanel: 'panel_4', direction: 'within' }, + params: { + valueA: 'test value', + }, }); const panel6 = event.api.addPanel({ id: 'panel_6', component: 'default', title: 'Panel 6', position: { referencePanel: 'panel_4', direction: 'below' }, + params: { + valueA: 'test value', + }, }); panel6.group.locked = true; panel6.group.header.hidden = true; @@ -75,6 +93,9 @@ const CustomHeadersDockview = () => { component: 'default', title: 'Panel 7', position: { referencePanel: 'panel_6', direction: 'right' }, + params: { + valueA: 'test value', + }, }); event.api.addPanel({ id: 'panel_8', @@ -82,6 +103,9 @@ const CustomHeadersDockview = () => { title: 'Panel 8', position: { referencePanel: 'panel_7', direction: 'within' }, + params: { + valueA: 'test value', + }, }); event.api.addGroup(); From dd70450a8e32d6bde94f958d93ba9759a6535fee Mon Sep 17 00:00:00 2001 From: mathuo <6710312+mathuo@users.noreply.github.com> Date: Mon, 3 Apr 2023 22:38:46 +0100 Subject: [PATCH 04/22] chore: docs --- .codesandbox/ci.json | 2 +- packages/docs/docs/components/dockview.mdx | 5 ++++- packages/docs/sandboxes/vanilla-dockview/src/app.ts | 2 +- packages/docs/sandboxes/vanilla-dockview/src/index.ts | 2 +- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/.codesandbox/ci.json b/.codesandbox/ci.json index d727a6c89..36d6935ec 100644 --- a/.codesandbox/ci.json +++ b/.codesandbox/ci.json @@ -25,4 +25,4 @@ "/packages/docs/sandboxes/watermark-dockview" ], "node": "16" -} \ No newline at end of file +} diff --git a/packages/docs/docs/components/dockview.mdx b/packages/docs/docs/components/dockview.mdx index 3e9910d0e..4b01b318d 100644 --- a/packages/docs/docs/components/dockview.mdx +++ b/packages/docs/docs/components/dockview.mdx @@ -720,4 +720,7 @@ The core library is published as an independant package under the name `dockview > When using `dockview` there is no need to also install `dockview-core`. > `dockview-core` is a dependency of `dockview` and automatically installed during the installation process of `dockview` via `npm install dockview`. - + diff --git a/packages/docs/sandboxes/vanilla-dockview/src/app.ts b/packages/docs/sandboxes/vanilla-dockview/src/app.ts index 79479e1ae..f1dc31cd6 100644 --- a/packages/docs/sandboxes/vanilla-dockview/src/app.ts +++ b/packages/docs/sandboxes/vanilla-dockview/src/app.ts @@ -2,7 +2,7 @@ import { DockviewComponent, IContentRenderer, IGroupPanelInitParameters, -} from 'dockview'; +} from 'dockview-core'; class DefaultPanel implements IContentRenderer { private _element: HTMLElement; diff --git a/packages/docs/sandboxes/vanilla-dockview/src/index.ts b/packages/docs/sandboxes/vanilla-dockview/src/index.ts index a1bf270a5..249b56017 100644 --- a/packages/docs/sandboxes/vanilla-dockview/src/index.ts +++ b/packages/docs/sandboxes/vanilla-dockview/src/index.ts @@ -1,5 +1,5 @@ import './styles.css'; -import 'dockview/dist/styles/dockview.css'; +import 'dockview-core/dist/styles/dockview.css'; import { attach } from './app'; From c4f778f1cc8f4b2890ff62ce30489c5189796043 Mon Sep 17 00:00:00 2001 From: mathuo <6710312+mathuo@users.noreply.github.com> Date: Thu, 6 Apr 2023 20:34:40 +0100 Subject: [PATCH 05/22] chore: improve docs --- README.md | 8 ++++---- packages/dockview-core/README.md | 14 ++++++-------- packages/dockview/README.md | 6 ++---- packages/docs/static/img/splashscreen.gif | Bin 0 -> 171942 bytes .../version-1.7.0/components/dockview.mdx | 9 ++++++--- 5 files changed, 18 insertions(+), 19 deletions(-) create mode 100644 packages/docs/static/img/splashscreen.gif diff --git a/README.md b/README.md index 214dc0e75..cf7eece3a 100644 --- a/README.md +++ b/README.md @@ -15,9 +15,9 @@ ## -Please see the website: https://dockview.dev +![](packages/docs/static/img/splashscreen.gif) -Want to inspect the latest deployment? Go to https://unpkg.com/browse/dockview@latest/ +Please see the website: https://dockview.dev ## Features @@ -29,11 +29,11 @@ Want to inspect the latest deployment? Go to https://unpkg.com/browse/dockview@l - Tabular docking and Drag and Drop support - Documentation and examples -This project was inspired by many popular IDE editors. Some parts of the core resizable panelling are inspired by code found in the VSCode codebase, [splitview](https://github.com/microsoft/vscode/tree/main/src/vs/base/browser/ui/splitview) and [gridview](https://github.com/microsoft/vscode/tree/main/src/vs/base/browser/ui/grid). +Want to inspect the latest deployment? Go to https://unpkg.com/browse/dockview@latest/ ## Quick start -Dockview has a peer dependency on `react >= 16.8.0` and `react-dom >= 16.8.0`. You can install dockview from [npm](https://www.npmjs.com/package/dockview). Please see the [Getting Started Guide](https://mathuo.github.io/dockview/docs/). +Dockview has a peer dependency on `react >= 16.8.0` and `react-dom >= 16.8.0`. You can install dockview from [npm](https://www.npmjs.com/package/dockview). ``` npm install --save dockview diff --git a/packages/dockview-core/README.md b/packages/dockview-core/README.md index 214dc0e75..57d14843e 100644 --- a/packages/dockview-core/README.md +++ b/packages/dockview-core/README.md @@ -1,7 +1,7 @@

dockview

-

Zero dependency layout manager supporting tabs, grids and splitviews with ReactJS support written in TypeScript

+

Zero dependency layout manager supporting tabs, grids and splitviews written in TypeScript

@@ -17,8 +17,6 @@ Please see the website: https://dockview.dev -Want to inspect the latest deployment? Go to https://unpkg.com/browse/dockview@latest/ - ## Features - Simple splitviews, nested splitviews (i.e. gridviews) supporting full layout managment with @@ -29,20 +27,20 @@ Want to inspect the latest deployment? Go to https://unpkg.com/browse/dockview@l - Tabular docking and Drag and Drop support - Documentation and examples -This project was inspired by many popular IDE editors. Some parts of the core resizable panelling are inspired by code found in the VSCode codebase, [splitview](https://github.com/microsoft/vscode/tree/main/src/vs/base/browser/ui/splitview) and [gridview](https://github.com/microsoft/vscode/tree/main/src/vs/base/browser/ui/grid). +Want to inspect the latest deployment? Go to https://unpkg.com/browse/dockview-core@latest/ ## Quick start -Dockview has a peer dependency on `react >= 16.8.0` and `react-dom >= 16.8.0`. You can install dockview from [npm](https://www.npmjs.com/package/dockview). Please see the [Getting Started Guide](https://mathuo.github.io/dockview/docs/). +You can install dockview-core from [npm](https://www.npmjs.com/package/dockview-core). ``` -npm install --save dockview +npm install --save dockview-core ``` -Within your project you must import or reference the stylesheet at `dockview/dist/styles/dockview.css` and attach a theme. +Within your project you must import or reference the stylesheet at `dockview-core/dist/styles/dockview.css` and attach a theme. ```css -@import '~dockview/dist/styles/dockview.css'; +@import '~dockview-core/dist/styles/dockview.css'; ``` You should also attach a dockview theme to an element containing your components. For example: diff --git a/packages/dockview/README.md b/packages/dockview/README.md index 214dc0e75..e96b3b5e3 100644 --- a/packages/dockview/README.md +++ b/packages/dockview/README.md @@ -17,8 +17,6 @@ Please see the website: https://dockview.dev -Want to inspect the latest deployment? Go to https://unpkg.com/browse/dockview@latest/ - ## Features - Simple splitviews, nested splitviews (i.e. gridviews) supporting full layout managment with @@ -29,11 +27,11 @@ Want to inspect the latest deployment? Go to https://unpkg.com/browse/dockview@l - Tabular docking and Drag and Drop support - Documentation and examples -This project was inspired by many popular IDE editors. Some parts of the core resizable panelling are inspired by code found in the VSCode codebase, [splitview](https://github.com/microsoft/vscode/tree/main/src/vs/base/browser/ui/splitview) and [gridview](https://github.com/microsoft/vscode/tree/main/src/vs/base/browser/ui/grid). +Want to inspect the latest deployment? Go to https://unpkg.com/browse/dockview@latest/ ## Quick start -Dockview has a peer dependency on `react >= 16.8.0` and `react-dom >= 16.8.0`. You can install dockview from [npm](https://www.npmjs.com/package/dockview). Please see the [Getting Started Guide](https://mathuo.github.io/dockview/docs/). +Dockview has a peer dependency on `react >= 16.8.0` and `react-dom >= 16.8.0`. You can install dockview from [npm](https://www.npmjs.com/package/dockview). ``` npm install --save dockview diff --git a/packages/docs/static/img/splashscreen.gif b/packages/docs/static/img/splashscreen.gif new file mode 100644 index 0000000000000000000000000000000000000000..df6b905578ab0b14329d2e387211b23ebedfc8ab GIT binary patch literal 171942 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¡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

ndPaO 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 M&)hO_bpomVKcjYjPXGV_ literal 0 HcmV?d00001 diff --git a/packages/docs/versioned_docs/version-1.7.0/components/dockview.mdx b/packages/docs/versioned_docs/version-1.7.0/components/dockview.mdx index f48ade881..4b01b318d 100644 --- a/packages/docs/versioned_docs/version-1.7.0/components/dockview.mdx +++ b/packages/docs/versioned_docs/version-1.7.0/components/dockview.mdx @@ -47,7 +47,7 @@ import { DockviewReact } from 'dockview'; ``` | Property | Type | Optional | Default | Description | -| --------------------- | ------------------------------------ | -------- | --------- | ------------------------------------------------------------ | --- | +| --------------------- | ------------------------------------ | -------- | --------- | ------------------------------------------------------------ | | onReady | (event: SplitviewReadyEvent) => void | No | | | | components | object | No | | | | tabComponents | object | Yes | | | @@ -58,7 +58,7 @@ import { DockviewReact } from 'dockview'; | onDidDrop | Event | Yes | false | | | showDndOverlay | Event | Yes | false | | | defaultTabComponent | object | Yes | | | -| groupControlComponent | object | Yes | | | | +| groupControlComponent | object | Yes | | | | singleTabMode | 'fullwidth' \| 'default' | Yes | 'default' | | ## Dockview API @@ -720,4 +720,7 @@ The core library is published as an independant package under the name `dockview > When using `dockview` there is no need to also install `dockview-core`. > `dockview-core` is a dependency of `dockview` and automatically installed during the installation process of `dockview` via `npm install dockview`. - + From 7b3adb919e63d20cd60dde8643be5735864419b6 Mon Sep 17 00:00:00 2001 From: mathuo <6710312+mathuo@users.noreply.github.com> Date: Tue, 11 Apr 2023 19:14:34 +0100 Subject: [PATCH 06/22] bug: flip orientation when removing intermediate node --- .../dockview/dockviewComponent.spec.ts | 2 +- .../src/__tests__/gridview/gridview.spec.ts | 95 ++++++++++++++++++- .../src/dockview/dockviewComponent.ts | 2 +- .../dockview-core/src/gridview/gridview.ts | 17 +++- 4 files changed, 111 insertions(+), 5 deletions(-) diff --git a/packages/dockview-core/src/__tests__/dockview/dockviewComponent.spec.ts b/packages/dockview-core/src/__tests__/dockview/dockviewComponent.spec.ts index 8ce3e9d32..253cd4f1e 100644 --- a/packages/dockview-core/src/__tests__/dockview/dockviewComponent.spec.ts +++ b/packages/dockview-core/src/__tests__/dockview/dockviewComponent.spec.ts @@ -1919,7 +1919,7 @@ describe('dockviewComponent', () => { id: 'group-1', activeView: 'panel1', }, - size: 1000, + size: 500, }, ], size: 1000, diff --git a/packages/dockview-core/src/__tests__/gridview/gridview.spec.ts b/packages/dockview-core/src/__tests__/gridview/gridview.spec.ts index d796b888a..79cdd3de5 100644 --- a/packages/dockview-core/src/__tests__/gridview/gridview.spec.ts +++ b/packages/dockview-core/src/__tests__/gridview/gridview.spec.ts @@ -1,5 +1,31 @@ -import { Gridview } from '../../gridview/gridview'; -import { Orientation } from '../../splitview/splitview'; +import { Emitter, Event } from '../../events'; +import { BranchNode } from '../../gridview/branchNode'; +import { + Gridview, + IGridView, + IViewSize, + orthogonal, +} from '../../gridview/gridview'; +import { Orientation, Sizing } from '../../splitview/splitview'; + +class MockGridview implements IGridView { + minimumWidth: number = 0; + maximumWidth: number = Number.MAX_VALUE; + minimumHeight: number = 0; + maximumHeight: number = Number.MAX_VALUE; + onDidChange: Event = new Emitter< + IViewSize | undefined + >().event; + element: HTMLElement = document.createElement('div'); + + layout(width: number, height: number): void { + // + } + + toJSON(): object { + return {}; + } +} describe('gridview', () => { let container: HTMLElement; @@ -25,4 +51,69 @@ describe('gridview', () => { expect(container.childNodes.length).toBe(0); }); + + test('insertOrthogonalSplitviewAtRoot #1', () => { + const gridview = new Gridview( + false, + { separatorBorder: '' }, + Orientation.HORIZONTAL + ); + gridview.layout(1000, 1000); + + gridview.addView(new MockGridview(), Sizing.Distribute, [0]); + + gridview.insertOrthogonalSplitviewAtRoot(); + + gridview.addView(new MockGridview(), Sizing.Distribute, [1]); + + gridview.addView(new MockGridview(), Sizing.Distribute, [0, 1]); + + function checkOrientationFlipsAtEachLevel(root: BranchNode) { + const orientation = root.orientation; + const orthogonalOrientation = orthogonal(orientation); + + for (const child of root.children) { + if (child.orientation !== orthogonalOrientation) { + fail('child cannot have the same orientation as parent'); + } + if (child instanceof BranchNode) { + checkOrientationFlipsAtEachLevel(child); + } + } + } + + checkOrientationFlipsAtEachLevel((gridview as any).root as BranchNode); + }); + + test('insertOrthogonalSplitviewAtRoot #2', () => { + const gridview = new Gridview( + false, + { separatorBorder: '' }, + Orientation.HORIZONTAL + ); + gridview.layout(1000, 1000); + + gridview.addView(new MockGridview(), Sizing.Distribute, [0]); + gridview.addView(new MockGridview(), Sizing.Distribute, [1]); + + gridview.insertOrthogonalSplitviewAtRoot(); + + gridview.addView(new MockGridview(), Sizing.Distribute, [1]); + + function checkOrientationFlipsAtEachLevel(root: BranchNode) { + const orientation = root.orientation; + const orthogonalOrientation = orthogonal(orientation); + + for (const child of root.children) { + if (child.orientation !== orthogonalOrientation) { + fail('child cannot have the same orientation as parent'); + } + if (child instanceof BranchNode) { + checkOrientationFlipsAtEachLevel(child); + } + } + } + + checkOrientationFlipsAtEachLevel((gridview as any).root as BranchNode); + }); }); diff --git a/packages/dockview-core/src/dockview/dockviewComponent.ts b/packages/dockview-core/src/dockview/dockviewComponent.ts index 0cb6ee8e1..6d58c0b67 100644 --- a/packages/dockview-core/src/dockview/dockviewComponent.ts +++ b/packages/dockview-core/src/dockview/dockviewComponent.ts @@ -717,7 +717,7 @@ export class DockviewComponent if (itemId === undefined) { if (sourceGroup) { - this.moveGroup(sourceGroup, referenceGroup, target); + this.moveGroup(sourceGroup, referenceGroup, target); } return; } diff --git a/packages/dockview-core/src/gridview/gridview.ts b/packages/dockview-core/src/gridview/gridview.ts index 2e45405c1..37242cc33 100644 --- a/packages/dockview-core/src/gridview/gridview.ts +++ b/packages/dockview-core/src/gridview/gridview.ts @@ -464,7 +464,22 @@ export class Gridview implements IDisposable { const childReference = oldRoot.children[0]; oldRoot.removeChild(0); // remove to prevent disposal when disposing of unwanted root oldRoot.dispose(); - this._root.addChild(childReference, Sizing.Distribute, 0); + + this._root.addChild( + /** + * the child node will have the same orientation as the new root since + * we are removing the inbetween node. + * the entire 'tree' must be flipped recursively to ensure that the orientation + * flips at each level + */ + flipNode( + childReference, + childReference.orthogonalSize, + childReference.size + ), + Sizing.Distribute, + 0 + ); } else { this._root.addChild(oldRoot, Sizing.Distribute, 0); } From 98c63628af782c1e2d22a7c4d66ff77fe4955182 Mon Sep 17 00:00:00 2001 From: mathuo <6710312+mathuo@users.noreply.github.com> Date: Tue, 11 Apr 2023 20:29:13 +0100 Subject: [PATCH 07/22] chore: remove sourcemaps from rollup jobs --- packages/dockview-core/package.json | 4 ++-- packages/dockview-core/rollup.config.js | 2 +- packages/dockview/package.json | 4 ++-- packages/dockview/rollup.config.js | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/dockview-core/package.json b/packages/dockview-core/package.json index e2a05b339..f429edff8 100644 --- a/packages/dockview-core/package.json +++ b/packages/dockview-core/package.json @@ -20,7 +20,7 @@ "build:esm": "cross-env ../../node_modules/.bin/tsc --project ./tsconfig.esm.json --extendedDiagnostics", "build:modulefiles": "rollup -c", "build": "npm run build:ci && npm run build:modulefiles", - "clean": "rimraf dist/ .build/", + "clean": "rimraf dist/ .build/ .rollup.cache/", "prepublishOnly": "npm run rebuild && npm run test", "docs": "typedoc", "rebuild": "npm run clean && npm run build", @@ -65,4 +65,4 @@ "rollup-plugin-postcss": "^4.0.2", "typedoc": "^0.23.25" } -} +} \ No newline at end of file diff --git a/packages/dockview-core/rollup.config.js b/packages/dockview-core/rollup.config.js index 7c8a12d45..a488a6ab2 100644 --- a/packages/dockview-core/rollup.config.js +++ b/packages/dockview-core/rollup.config.js @@ -55,12 +55,12 @@ function createBundle(format, options) { ` * @license ${license}`, ` */`, ].join('\n'), - sourcemap: true, }; const plugins = [ typescript({ tsconfig: 'tsconfig.esm.json', + sourceMap: false, }), ]; diff --git a/packages/dockview/package.json b/packages/dockview/package.json index dce0840c5..a3d232cea 100644 --- a/packages/dockview/package.json +++ b/packages/dockview/package.json @@ -20,7 +20,7 @@ "build:esm": "cross-env ../../node_modules/.bin/tsc --project ./tsconfig.esm.json --extendedDiagnostics", "build:modulefiles": "rollup -c", "build": "npm run build:ci && npm run build:modulefiles", - "clean": "rimraf dist/ .build/", + "clean": "rimraf dist/ .build/ .rollup.cache/", "docs": "typedoc", "prepublishOnly": "npm run rebuild && npm run test", "rebuild": "npm run clean && npm run build", @@ -74,4 +74,4 @@ "rollup-plugin-postcss": "^4.0.2", "typedoc": "^0.23.25" } -} +} \ No newline at end of file diff --git a/packages/dockview/rollup.config.js b/packages/dockview/rollup.config.js index b522bd0c0..60b1c15b5 100644 --- a/packages/dockview/rollup.config.js +++ b/packages/dockview/rollup.config.js @@ -56,7 +56,6 @@ function createBundle(format, options) { ` * @license ${license}`, ` */`, ].join('\n'), - sourcemap: true, }; const plugins = [ @@ -65,6 +64,7 @@ function createBundle(format, options) { }), typescript({ tsconfig: 'tsconfig.esm.json', + sourceMap: false, }), ]; From d20689170d0238701bf858c6625d14e6b51e8212 Mon Sep 17 00:00:00 2001 From: mathuo <6710312+mathuo@users.noreply.github.com> Date: Tue, 11 Apr 2023 20:43:38 +0100 Subject: [PATCH 08/22] chore: remove source maps from rollup jobs --- packages/dockview-core/rollup.config.js | 5 ++++- packages/dockview/rollup.config.js | 4 +++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/dockview-core/rollup.config.js b/packages/dockview-core/rollup.config.js index a488a6ab2..cd38f7e4b 100644 --- a/packages/dockview-core/rollup.config.js +++ b/packages/dockview-core/rollup.config.js @@ -57,10 +57,13 @@ function createBundle(format, options) { ].join('\n'), }; + const plugins = [ typescript({ tsconfig: 'tsconfig.esm.json', - sourceMap: false, + compilerOptions: { + declaration: false, + }, }), ]; diff --git a/packages/dockview/rollup.config.js b/packages/dockview/rollup.config.js index 60b1c15b5..7554e9fb1 100644 --- a/packages/dockview/rollup.config.js +++ b/packages/dockview/rollup.config.js @@ -64,7 +64,9 @@ function createBundle(format, options) { }), typescript({ tsconfig: 'tsconfig.esm.json', - sourceMap: false, + compilerOptions: { + declaration: false, + }, }), ]; From c1edea87448260013b0acd5f3d3f8d997b0efa5d Mon Sep 17 00:00:00 2001 From: mathuo <6710312+mathuo@users.noreply.github.com> Date: Tue, 11 Apr 2023 21:04:34 +0100 Subject: [PATCH 09/22] docs: 1.7.1 --- .codesandbox/ci.json | 3 +- .../docs/blog/2023-04-11-dockview-1.7.1.md | 26 + packages/docs/docs/basics.mdx | 119 --- packages/docs/docs/components/dockview.mdx | 7 + packages/docs/docs/example.mdx | 8 + .../sandboxes/tabheight-dockview/package.json | 31 + .../tabheight-dockview/public/index.html | 44 ++ .../sandboxes/tabheight-dockview/src/app.scss | 3 + .../sandboxes/tabheight-dockview/src/app.tsx | 83 ++ .../tabheight-dockview/src/index.tsx | 20 + .../tabheight-dockview/src/styles.css | 16 + .../tabheight-dockview/tsconfig.json | 20 + .../versioned_docs/version-1.7.0/basics.mdx | 119 --- .../version-1.7.1/components/_category_.json | 9 + .../version-1.7.1/components/dockview.mdx | 733 ++++++++++++++++++ .../version-1.7.1/components/gridview.mdx | 120 +++ .../version-1.7.1/components/paneview.mdx | 285 +++++++ .../version-1.7.1/components/splitview.mdx | 246 ++++++ .../versioned_docs/version-1.7.1/example.mdx | 8 + .../versioned_docs/version-1.7.1/index.mdx | 149 ++++ .../versioned_docs/version-1.7.1/theme.mdx | 89 +++ .../version-1.7.1-sidebars.json | 8 + packages/docs/versions.json | 1 + 23 files changed, 1908 insertions(+), 239 deletions(-) create mode 100644 packages/docs/blog/2023-04-11-dockview-1.7.1.md delete mode 100644 packages/docs/docs/basics.mdx create mode 100644 packages/docs/docs/example.mdx create mode 100644 packages/docs/sandboxes/tabheight-dockview/package.json create mode 100644 packages/docs/sandboxes/tabheight-dockview/public/index.html create mode 100644 packages/docs/sandboxes/tabheight-dockview/src/app.scss create mode 100644 packages/docs/sandboxes/tabheight-dockview/src/app.tsx create mode 100644 packages/docs/sandboxes/tabheight-dockview/src/index.tsx create mode 100644 packages/docs/sandboxes/tabheight-dockview/src/styles.css create mode 100644 packages/docs/sandboxes/tabheight-dockview/tsconfig.json delete mode 100644 packages/docs/versioned_docs/version-1.7.0/basics.mdx create mode 100644 packages/docs/versioned_docs/version-1.7.1/components/_category_.json create mode 100644 packages/docs/versioned_docs/version-1.7.1/components/dockview.mdx create mode 100644 packages/docs/versioned_docs/version-1.7.1/components/gridview.mdx create mode 100644 packages/docs/versioned_docs/version-1.7.1/components/paneview.mdx create mode 100644 packages/docs/versioned_docs/version-1.7.1/components/splitview.mdx create mode 100644 packages/docs/versioned_docs/version-1.7.1/example.mdx create mode 100644 packages/docs/versioned_docs/version-1.7.1/index.mdx create mode 100644 packages/docs/versioned_docs/version-1.7.1/theme.mdx create mode 100644 packages/docs/versioned_sidebars/version-1.7.1-sidebars.json diff --git a/.codesandbox/ci.json b/.codesandbox/ci.json index 36d6935ec..4d276169b 100644 --- a/.codesandbox/ci.json +++ b/.codesandbox/ci.json @@ -20,9 +20,10 @@ "/packages/docs/sandboxes/resize-dockview", "/packages/docs/sandboxes/resizecontainer-dockview", "/packages/docs/sandboxes/simple-dockview", + "/packages/docs/sandboxes/tabheight-dockview", "/packages/docs/sandboxes/updatetitle-dockview", "/packages/docs/sandboxes/vanilla-dockview", "/packages/docs/sandboxes/watermark-dockview" ], "node": "16" -} +} \ No newline at end of file diff --git a/packages/docs/blog/2023-04-11-dockview-1.7.1.md b/packages/docs/blog/2023-04-11-dockview-1.7.1.md new file mode 100644 index 000000000..8fb604d55 --- /dev/null +++ b/packages/docs/blog/2023-04-11-dockview-1.7.1.md @@ -0,0 +1,26 @@ +--- +slug: dockview-1.7.1-release +title: Dockview 1.7.1 +tags: [release] +--- + +# Release Notes + +Please reference to docs @ [dockview.dev](https://dockview.dev). +If you feel anything is missing or unclear please let me know. + +## 🚀 Features + +- Resize observer [#227](https://github.com/mathuo/dockview/pull/227) +- Minor type fix [#237](https://github.com/mathuo/dockview/pull/237) + +## 🛠 Miscs + +- Additional documentation and examples [#217](https://github.com/mathuo/dockview/pull/217) [#221](https://github.com/mathuo/dockview/pull/221) [#228](https://github.com/mathuo/dockview/pull/228) [#229](https://github.com/mathuo/dockview/pull/229) [#240](https://github.com/mathuo/dockview/pull/240) [#241](https://github.com/mathuo/dockview/pull/241) +- Adjust build configurations [#223](https://github.com/mathuo/dockview/pull/223) [#235](https://github.com/mathuo/dockview/pull/235) [#244](https://github.com/mathuo/dockview/pull/244) + +## 🔥 Breaking changes + +- Fix close button on default watermark [#225](https://github.com/mathuo/dockview/pull/225) +- Remove tab height control as prop to `DockviewReact` component. Please control via CSS instead, see docs for tab height. [#236](https://github.com/mathuo/dockview/pull/236) +- Fix edge-case bug when dropping a panel on far corners [#243](https://github.com/mathuo/dockview/pull/243) diff --git a/packages/docs/docs/basics.mdx b/packages/docs/docs/basics.mdx deleted file mode 100644 index 91fdcbfd9..000000000 --- a/packages/docs/docs/basics.mdx +++ /dev/null @@ -1,119 +0,0 @@ ---- -sidebar_position: 1 -description: How to get started with Dockview ---- - -import { SimpleSplitview } from '@site/src/components/simpleSplitview'; -import { SimpleSplitview2 } from '@site/src/components/simpleSplitview2'; - -# Basics - -asd -This section will take you through a number of concepts that can be applied to all dockview components. - -## Panels - -The below examples use `ReactSplitview` but the logic holds for `ReactPaneview`, `ReactGridview` and `ReactDockview` using their respective implementations and interfaces. -All components require you to provide an `onReady` prop which you can use to build and control your component. - -### Adding a panel with parameters - -You can pass parameters to a panel through the `params` object - -```tsx -const onReady = (event: SplitviewReadyEvent) => { - event.api.addPanel({ - id: 'panel_1', - component: 'myComponent', - params: { - title: 'My Title', - }, - }); -}; -``` - -and you can access those properties through the `props.params` object. The TypeScript interface accepts an optional generic type `T` that corresponds to the params objects type. - -```tsx -const MyComponent = (props: ISplitviewPanelProps<{ title: string }>) => { - return

{`My first panel has the title: ${props.params.title}`}
; -}; -``` - -## API - -There are two types of API you will interact with using `dockview`. - -- The `panel API` is accessible via `props.api` in user defined panels and via the `.api` variable found on panel instances. This API contains actions and variable related to the the individual panel. -- The `container API` is accessible via `event.api` in the `onReady` events and `props.containerApi` in user defined panels. This API contains actions and variable related to the component as a whole. - -```tsx -const MyComponent = (props: ISplitviewPanelProps<{ title: string }>) => { - React.useEffect(() => { - const disposable = props.api.onDidActiveChange((event) => { - console.log(`is panel active: ${event.isActive}`); - }); - - return () => { - disposable.dispose(); // remember to dispose of any subscriptions - }; - }, [props.api]); - - const addAnotherPanel = React.useCallback(() => { - props.containerApi.addPanel({ - id: 'another_id', - component: 'anotherComponent', - }); - }, [props.containerApi]); - - return ( -
- {`My first panel has the title: ${props.params.title}`} - -
- ); -}; -``` - -### Serialization - -All components support `toJSON(): T` which returns a Typed object representation of the components state. This same Typed object can be used to deserialize a view using `fromJSON(object: T): void`. - -## Auto resizing - -`SplitviewReact`, `GridviewReact`, `PaneviewReact` and `DockviewReact` will all automatically resize to fill the size of their parent element. -Internally this is achieved using a [ResizeObserver](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver) which some users may need to polyfill. -You can disable this by settings the `disableAutoResizing` prop to be `true`. - -You can manually resize a component using the API method `layout(width: number, height: number): void`. -An advanced case may use this in conjunction with `disableAutoResizing=true` to allow a parent component to have ultimate control over the dimensions of the component. - -## Events - -Many API properties can be listened on using the `Event` pattern. For example `api.onDidFocusChange(() => {...})`. -You should dispose of any event listeners you create cleaning up any listeners you would have created. - -```tsx -React.useEffect(() => { - const disposable = api.onDidFocusChange(() => { - // write some code - }); - - return () => { - disposable.dispose(); - }; -}, []); -``` - -## Proportional layout - -The `proportionalLayout` property indicates the expected behaviour of the component as it's container resizes, should all views resize equally or should just one view expand to fill the new space. `proportionalLayout` can be set as a property on `SplitviewReact` and `GridviewReact` components. -Although not configurable on `DockviewReact` and `PaneviewReact` these both behave as if `proportionalLayout=true` was set for them. - - - - - -## Browser support - -dockview is intended to support all major browsers. Some users may require a polyfill for [ResizeObserver](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver). diff --git a/packages/docs/docs/components/dockview.mdx b/packages/docs/docs/components/dockview.mdx index 4b01b318d..b6d1a3e46 100644 --- a/packages/docs/docs/components/dockview.mdx +++ b/packages/docs/docs/components/dockview.mdx @@ -23,6 +23,7 @@ import DockviewSetTitle from '@site/sandboxes/updatetitle-dockview/src/app'; import RenderingDockview from '@site/sandboxes/rendering-dockview/src/app'; 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 { attach as attachDockviewVanilla } from '@site/sandboxes/vanilla-dockview/src/app'; # Dockview @@ -609,6 +610,12 @@ to the entire width of the group. For example:
+### Tab Height + + + + + ## Groups ### Locked group diff --git a/packages/docs/docs/example.mdx b/packages/docs/docs/example.mdx new file mode 100644 index 000000000..29989cd51 --- /dev/null +++ b/packages/docs/docs/example.mdx @@ -0,0 +1,8 @@ +import Dockview from '@site/sandboxes/advanced-example-dockview/src/app'; +import useBaseUrl from '@docusaurus/useBaseUrl'; + +import { Container } from '@site/src/components/ui/container'; + + + + diff --git a/packages/docs/sandboxes/tabheight-dockview/package.json b/packages/docs/sandboxes/tabheight-dockview/package.json new file mode 100644 index 000000000..af6d19d63 --- /dev/null +++ b/packages/docs/sandboxes/tabheight-dockview/package.json @@ -0,0 +1,31 @@ +{ + "name": "tabheight-dockview", + "description": "", + "keywords": [ + "dockview" + ], + "version": "1.0.0", + "main": "src/index.tsx", + "dependencies": { + "dockview": "*", + "react": "^18.2.0", + "react-dom": "^18.2.0" + }, + "devDependencies": { + "@types/react": "^18.0.28", + "@types/react-dom": "^18.0.11", + "typescript": "^4.9.5" + }, + "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" + ] +} diff --git a/packages/docs/sandboxes/tabheight-dockview/public/index.html b/packages/docs/sandboxes/tabheight-dockview/public/index.html new file mode 100644 index 000000000..1f8a52426 --- /dev/null +++ b/packages/docs/sandboxes/tabheight-dockview/public/index.html @@ -0,0 +1,44 @@ + + + + + + + + + + + + + React App + + + + +
+ + + + diff --git a/packages/docs/sandboxes/tabheight-dockview/src/app.scss b/packages/docs/sandboxes/tabheight-dockview/src/app.scss new file mode 100644 index 000000000..315d564ac --- /dev/null +++ b/packages/docs/sandboxes/tabheight-dockview/src/app.scss @@ -0,0 +1,3 @@ +.skinny-tabs { + --dv-tabs-and-actions-container-height: 20px; +} diff --git a/packages/docs/sandboxes/tabheight-dockview/src/app.tsx b/packages/docs/sandboxes/tabheight-dockview/src/app.tsx new file mode 100644 index 000000000..bd053a990 --- /dev/null +++ b/packages/docs/sandboxes/tabheight-dockview/src/app.tsx @@ -0,0 +1,83 @@ +import { + DockviewReact, + DockviewReadyEvent, + IDockviewPanelProps, +} from 'dockview'; +import * as React from 'react'; +import './app.scss'; + +const components = { + default: (props: IDockviewPanelProps<{ title: string }>) => { + return ( +
+ {props.params.title} +
+ ); + }, +}; + +export const App: React.FC = () => { + const onReady = (event: DockviewReadyEvent) => { + event.api.addPanel({ + id: 'panel_1', + component: 'default', + params: { + title: 'Panel 1', + }, + }); + + event.api.addPanel({ + id: 'panel_2', + component: 'default', + params: { + title: 'Panel 2', + }, + }); + + event.api.addPanel({ + id: 'panel_3', + component: 'default', + params: { + title: 'Panel 3', + }, + position: { referencePanel: 'panel_1', direction: 'right' }, + }); + + event.api.addPanel({ + id: 'panel_4', + component: 'default', + params: { + title: 'Panel 4', + }, + position: { referencePanel: 'panel_3', direction: 'right' }, + }); + + event.api.addPanel({ + id: 'panel_5', + component: 'default', + params: { + title: 'Panel 5', + }, + position: { referencePanel: 'panel_4', direction: 'below' }, + }); + + event.api.addPanel({ + id: 'panel_6', + component: 'default', + params: { + title: 'Panel 6', + }, + position: { referencePanel: 'panel_5', direction: 'right' }, + }); + }; + + return ( + + ); +}; + +export default App; diff --git a/packages/docs/sandboxes/tabheight-dockview/src/index.tsx b/packages/docs/sandboxes/tabheight-dockview/src/index.tsx new file mode 100644 index 000000000..2fe1be232 --- /dev/null +++ b/packages/docs/sandboxes/tabheight-dockview/src/index.tsx @@ -0,0 +1,20 @@ +import { StrictMode } from 'react'; +import * as ReactDOMClient from 'react-dom/client'; +import './styles.css'; +import 'dockview/dist/styles/dockview.css'; + +import App from './app'; + +const rootElement = document.getElementById('root'); + +if (rootElement) { + const root = ReactDOMClient.createRoot(rootElement); + + root.render( + +
+ +
+
+ ); +} diff --git a/packages/docs/sandboxes/tabheight-dockview/src/styles.css b/packages/docs/sandboxes/tabheight-dockview/src/styles.css new file mode 100644 index 000000000..a1d49a9b6 --- /dev/null +++ b/packages/docs/sandboxes/tabheight-dockview/src/styles.css @@ -0,0 +1,16 @@ +body { + margin: 0px; + color: white; + font-family: sans-serif; + text-align: center; +} + +#root { + height: 100vh; + width: 100vw; +} + +.app { + height: 100%; +} + diff --git a/packages/docs/sandboxes/tabheight-dockview/tsconfig.json b/packages/docs/sandboxes/tabheight-dockview/tsconfig.json new file mode 100644 index 000000000..6e13e47b5 --- /dev/null +++ b/packages/docs/sandboxes/tabheight-dockview/tsconfig.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "outDir": "build/dist", + "module": "esnext", + "target": "es5", + "lib": ["es6", "dom"], + "sourceMap": true, + "allowJs": true, + "jsx": "react-jsx", + "moduleResolution": "node", + "rootDir": "src", + "forceConsistentCasingInFileNames": true, + "noImplicitReturns": true, + "noImplicitThis": true, + "noImplicitAny": true, + "strictNullChecks": true, + "suppressImplicitAnyIndexErrors": true, + "noUnusedLocals": true + } +} diff --git a/packages/docs/versioned_docs/version-1.7.0/basics.mdx b/packages/docs/versioned_docs/version-1.7.0/basics.mdx deleted file mode 100644 index 91fdcbfd9..000000000 --- a/packages/docs/versioned_docs/version-1.7.0/basics.mdx +++ /dev/null @@ -1,119 +0,0 @@ ---- -sidebar_position: 1 -description: How to get started with Dockview ---- - -import { SimpleSplitview } from '@site/src/components/simpleSplitview'; -import { SimpleSplitview2 } from '@site/src/components/simpleSplitview2'; - -# Basics - -asd -This section will take you through a number of concepts that can be applied to all dockview components. - -## Panels - -The below examples use `ReactSplitview` but the logic holds for `ReactPaneview`, `ReactGridview` and `ReactDockview` using their respective implementations and interfaces. -All components require you to provide an `onReady` prop which you can use to build and control your component. - -### Adding a panel with parameters - -You can pass parameters to a panel through the `params` object - -```tsx -const onReady = (event: SplitviewReadyEvent) => { - event.api.addPanel({ - id: 'panel_1', - component: 'myComponent', - params: { - title: 'My Title', - }, - }); -}; -``` - -and you can access those properties through the `props.params` object. The TypeScript interface accepts an optional generic type `T` that corresponds to the params objects type. - -```tsx -const MyComponent = (props: ISplitviewPanelProps<{ title: string }>) => { - return
{`My first panel has the title: ${props.params.title}`}
; -}; -``` - -## API - -There are two types of API you will interact with using `dockview`. - -- The `panel API` is accessible via `props.api` in user defined panels and via the `.api` variable found on panel instances. This API contains actions and variable related to the the individual panel. -- The `container API` is accessible via `event.api` in the `onReady` events and `props.containerApi` in user defined panels. This API contains actions and variable related to the component as a whole. - -```tsx -const MyComponent = (props: ISplitviewPanelProps<{ title: string }>) => { - React.useEffect(() => { - const disposable = props.api.onDidActiveChange((event) => { - console.log(`is panel active: ${event.isActive}`); - }); - - return () => { - disposable.dispose(); // remember to dispose of any subscriptions - }; - }, [props.api]); - - const addAnotherPanel = React.useCallback(() => { - props.containerApi.addPanel({ - id: 'another_id', - component: 'anotherComponent', - }); - }, [props.containerApi]); - - return ( -
- {`My first panel has the title: ${props.params.title}`} - -
- ); -}; -``` - -### Serialization - -All components support `toJSON(): T` which returns a Typed object representation of the components state. This same Typed object can be used to deserialize a view using `fromJSON(object: T): void`. - -## Auto resizing - -`SplitviewReact`, `GridviewReact`, `PaneviewReact` and `DockviewReact` will all automatically resize to fill the size of their parent element. -Internally this is achieved using a [ResizeObserver](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver) which some users may need to polyfill. -You can disable this by settings the `disableAutoResizing` prop to be `true`. - -You can manually resize a component using the API method `layout(width: number, height: number): void`. -An advanced case may use this in conjunction with `disableAutoResizing=true` to allow a parent component to have ultimate control over the dimensions of the component. - -## Events - -Many API properties can be listened on using the `Event` pattern. For example `api.onDidFocusChange(() => {...})`. -You should dispose of any event listeners you create cleaning up any listeners you would have created. - -```tsx -React.useEffect(() => { - const disposable = api.onDidFocusChange(() => { - // write some code - }); - - return () => { - disposable.dispose(); - }; -}, []); -``` - -## Proportional layout - -The `proportionalLayout` property indicates the expected behaviour of the component as it's container resizes, should all views resize equally or should just one view expand to fill the new space. `proportionalLayout` can be set as a property on `SplitviewReact` and `GridviewReact` components. -Although not configurable on `DockviewReact` and `PaneviewReact` these both behave as if `proportionalLayout=true` was set for them. - - - - - -## Browser support - -dockview is intended to support all major browsers. Some users may require a polyfill for [ResizeObserver](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver). diff --git a/packages/docs/versioned_docs/version-1.7.1/components/_category_.json b/packages/docs/versioned_docs/version-1.7.1/components/_category_.json new file mode 100644 index 000000000..366718010 --- /dev/null +++ b/packages/docs/versioned_docs/version-1.7.1/components/_category_.json @@ -0,0 +1,9 @@ +{ + "label": "Components", + "collapsible": true, + "collapsed": false, + "link": { + "type": "generated-index", + "title": "Components" + } +} \ No newline at end of file diff --git a/packages/docs/versioned_docs/version-1.7.1/components/dockview.mdx b/packages/docs/versioned_docs/version-1.7.1/components/dockview.mdx new file mode 100644 index 000000000..b6d1a3e46 --- /dev/null +++ b/packages/docs/versioned_docs/version-1.7.1/components/dockview.mdx @@ -0,0 +1,733 @@ +--- +description: Dockview Documentation +--- + +import { Container } from '@site/src/components/ui/container'; + +import Link from '@docusaurus/Link'; +import useBaseUrl from '@docusaurus/useBaseUrl'; + +import DockviewPersistance 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 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/groupcontrol-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'; +import DockviewSetTitle from '@site/sandboxes/updatetitle-dockview/src/app'; +import RenderingDockview from '@site/sandboxes/rendering-dockview/src/app'; +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 { attach as attachDockviewVanilla } from '@site/sandboxes/vanilla-dockview/src/app'; + +# Dockview + +## Introduction + +Dockview is an abstraction built on top of [Gridviews](./gridview) where each view is a container of many tabbed panels. + + + + + +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. + +## DockviewReact Component + +You can create a Dockview through the use of the `DockviewReact` component. + +```tsx +import { DockviewReact } from 'dockview'; +``` + +| Property | Type | Optional | Default | Description | +| --------------------- | ------------------------------------ | -------- | --------- | ------------------------------------------------------------ | +| onReady | (event: SplitviewReadyEvent) => void | No | | | +| components | object | No | | | +| tabComponents | object | Yes | | | +| watermarkComponent | object | Yes | | | +| hideBorders | boolean | Yes | false | | +| className | string | Yes | '' | | +| disableAutoResizing | boolean | Yes | false | See Auto Resizing | +| onDidDrop | Event | Yes | false | | +| showDndOverlay | Event | Yes | false | | +| defaultTabComponent | object | Yes | | | +| groupControlComponent | object | Yes | | | +| singleTabMode | 'fullwidth' \| 'default' | Yes | 'default' | | + +## 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
{`My first panel has the title: ${props.params.title}`}
; +}; +``` + +```tsx title="Dockview API via the onReady callback" +const onReady = (event: DockviewReadyEvent) => { + // event.api... +}; +``` + +| Property | Type | Description | +| ---------------------- | ---------------------------------------------------- | -------------------------------------------------------- | +| height | `number` | Component pixel height | +| width | `number` | Component pixel width | +| minimumHeight | `number` | | +| maximumHeight | `number` | | +| maximumWidth | `number` | | +| maximumWidth | `number` | | +| length | `number` | Number of panels | +| size | `number` | Number of Groups | +| panels | `IDockviewPanel[]` | | +| groups | `GroupPanel[]` | | +| activePanel | `IDockviewPanel \| undefined` | | +| activeGroup | `IDockviewPanel \| undefined` | | +| | | | +| onDidLayoutChange | `Event` | | +| onDidLayoutFromJSON | `Event` | | +| onDidAddGroup | `Event` | | +| onDidRemoveGroup | `Event` | | +| onDidActiveGroupChange | `Event` | | +| onDidAddPanel | `Event` | | +| onDidRemovePanel | `Event` | | +| onDidActivePanelChange | `Event` | | +| onDidDrop | `EventAuto Resizing | +| fromJSON | `(data: SerializedDockview): void` | Serialization | +| toJSON | `(): SerializedDockview` | Serialization | +| clear | `(): void` | Clears the current layout | + +## Dockview Panel API + +```tsx +const MyComponent = (props: IDockviewPanelProps<{ title: string }>) => { + // props.api... + + return
{`My first panel has the title: ${props.params.title}`}
; +}; +``` + +| Property | Type | Description | +| ---------------------- | ----------------------------------------------------------- | ---------------- | +| id | `string` | Panel id | +| isFocused | `boolean` | Is panel focused | +| isActive | `boolean` | Is panel active | +| width | `number` | Panel width | +| height | `number` | Panel height | +| onDidDimensionsChange | `Event` | | +| onDidFocusChange | `Event` | | +| onDidVisibilityChange | `Event` | | +| onDidActiveChange | `Event` | | +| setActive | `(): void` | | +| | | | +| onDidConstraintsChange | `onDidConstraintsChange: Event` | | +| setConstraints | `(value: PanelConstraintChangeEvent2): void;` | | +| setSize | `(event: SizeEvent): void` | | +| | | | +| group | `GroupPanel | undefined` | +| isGroupActive | `boolean` | | +| title | `string` | | +| suppressClosable | `boolean` | | +| close | `(): void` | | +| setTitle | `(title: string): void` | | + +## Layout Persistance + +Layouts are loaded and saved via to `fromJSON` and `toJSON` methods on the Dockview api. +The api also exposes an event `onDidLayoutChange` you can listen on to determine when the layout has changed. +Below are some snippets showing how you might load from and save to localStorage. + +```tsx title="Saving the layout state to localStorage" +React.useEffect(() => { + if (!api) { + return; + } + + const disposable = api.onDidLayoutChange(() => { + const layout = api.toJSON(); + + localStorage.setItem( + 'dockview_persistance_layout', + JSON.stringify(layout) + ); + }); + + return () => { + disposable.dispose(); + }; +}, [api]); +``` + +```tsx title="Loading a layout from localStorage" +const onReady = (event: DockviewReadyEvent) => { + const layoutString = localStorage.getItem('dockview_persistance_layout'); + + let success = false; + + if (layoutString) { + try { + const layout = JSON.parse(layoutString); + event.api.fromJSON(layout); + success = true; + } catch (err) { + // + } + } + + if (!success) { + // do something if there is no layout or there was a loading error + } +}; +``` + +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. + + + + + +## Resizing + +### 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. + + + + + +### Container Resizing + +The component will automatically resize to it's container. + + + + + +## Watermark + +When the dockview is empty you may want to display some fallback content, this is refered to as the `watermark`. +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. + + + + + +## Drag And Drop + +### Built-in behaviours + +Dockview supports a wide variety of built-in Drag and Drop possibilities. +Below are some examples of the operations you can perform. + + + +> Drag a tab onto another tab to place it inbetween existing tabs. + + + +> Drag a tab to the right of the last tab to place it after the existing tabs. + + + +> Drag a group onto an existing group to merge the two groups. + +
+ + +
+ +> Drag into the left/right/top/bottom target zone of a panel to create a new group in the selected direction. + +> Drag into the center of a panel to add to that group. + +> Drag to the edge of the dockview component to create a new group on the selected edge. + +### 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 ( + +); +``` + + + + + +### Third Party Dnd Libraries + +To be completed... + + + + + +## Panels + +### Add Panel + +Using the dockview API you can access the `addPanel` method which returns an instance of the created panel. +The minimum method signature is: + +```ts +const panel = api.addPanel({ + id: 'my_unique_panel_id', + component: 'my_component', +}); +``` + +where `id` is the unique id of the panel and `component` is the implenentation which +will be used to render the panel. You will have registered this using the `components` prop of the `DockviewReactComponent` component. + +You can optionally provide a `tabComponent` parameters to the `addPanel` method which will render the tab using a custom renderer. +You will have registered this using the `tabComponents` prop of the `DockviewReactComponent` component. + +```ts +const panel = api.addPanel({ + id: 'my_unique_panel_id', + component: 'my_component', + tabComponent: 'my_tab_component', +}); +``` + +You can pass properties to the panel using the `params` key. +You can update these properties through the panels `api` object and its `updateParameters` method. + +```ts +const panel = api.addPanel({ + id: 'my_unique_panel_id', + component: 'my_component', + params: { + myCustomKey: 'my_custom_value', + }, +}); + +panel.api.updateParameters({ + myCustomKey: 'my_custom_value', + myOtherCustomKey: 'my_other_custom_key', +}); +``` + +> Note `updateParameters` does not accept partial parameter updates, you should call it with the entire set of parameters +> you want the panel to receive. + +Finally `addPanel` accepts a `position` object which tells dockview where to place the panel. + +- This object optionally accepts either a `referencePanel` or `referenceGroup` which can be the associated id as a string + or the panel/group object reference. +- This object accepts a `direction` property which dictates where, + relative to the provided reference the new panel will be placed. + +> If neither a `referencePanel` or `referenceGroup` then the provided `direction` will be treated as absolute. + +> If no `direction` is provided the library will place the new panel in a pre-determined position. + +```ts +const panel = api.addPanel({ + id: 'panel_1', + component: 'default', +}); + +const panel2 = api.addPanel({ + id: 'panel_2', + component: 'default', + position: { + referencePanel: panel1, + direction: 'right', + }, +}); +``` + +### Panel Rendering + +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. + +However 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. + +> For example 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. + +This is the default behaviour to ensure the greatest flexibility for the user but through the panels `props.api` you can listen to the visiblity state of the panel +and write additional logic to optimize your application. + +For example 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 +) { + const HigherOrderComponent = (props: IDockviewPanelProps) => { + const [visible, setVisible] = React.useState( + 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. + + + + + +## Headers + +### Custom Tab Headers + +You can provide custom renderers for your tab headers for maximum customization. +A default implementation of `DockviewDefaultTab` is provided should you only wish to attach minor +changes and events that do not alter the default behaviour, for example to add a custom context menu event +handler. + +```tsx title="Attaching a custom context menu event handlers to a custom header" +import { IDockviewPanelHeaderProps, DockviewDefaultTab } from 'dockview'; + +const MyCustomheader = (props: IDockviewPanelHeaderProps) => { + const onContextMenu = (event: React.MouseEvent) => { + event.preventDefault(); + alert('context menu'); + }; + return ; +}; +``` + +You are also free to define a custom renderer entirely from scratch and not make use of the `DockviewDefaultTab` component. +To use a custom renderer you can must register a collection of tab components. + +```tsx +const tabComponents = { + myCustomHeader: MyCustomHeader, +}; + +return ; +``` + +```tsx +api.addPanel({ + id: 'panel_1', + component: 'default', + tabComponent: 'myCustomHeader', // <-- your registered renderers + title: 'Panel 1', +}); +``` + +You can also override the default tab renderer which will be used when no `tabComponent` is provided to the `addPanel` function. + +```tsx +; +``` + +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. + + + + + +### Default Tab Title + +If you are using the default tab renderer you can set the title of a tab when creating it + +```tsx +api.addPanel({ + id: 'panel_1', + component: 'my_component', + title: 'my_custom_title', // <-- special param for title +}); +``` + +You can update the title through the panel api which can be accessed via `props.api` if you are inside the panel +component or via `api.getPanel('panel1').api` if you are accessing from outside of the panel component. + +```tsx +api.setTitle('my_new_custom_title'); +``` + +> Note this only works when using the default tab implementation. + + + + + +### Custom Tab Title + +If you are using a custom tab implementation you should pass variables through as a parameter and render them +through your tab components implementation. + +```tsx title="Add a panel with custom parameters" +api.addPanel({ + id: 'panel_2', + component: 'my_component', + tabComponent: 'my_tab', + params: { + myTitle: 'Window 2', // <-- passing a variable to use as a title + }, +}); +``` + +```tsx title="Accessing custom parameters from a custom tab renderer" +const tabComponents = { + default: (props: IDockviewPanelHeaderProps<{ myTitle: string }>) => { + const title = props.params.myTitle; // <-- accessing my custom varaible + return
{/** tab implementation as chosen by developer */}
; + }, +}; +``` + +### Hidden Headers + +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; +``` + +### Full width tabs + +`DockviewReactComponent` accepts the prop `singleTabMode`. If set `singleTabMode=fullwidth` then when there is only one tab in a group this tab will expand +to the entire width of the group. For example: + +> This can be conmbined with Locked Groups to create an application that feels more like a Window Manager +> rather than a collection of groups and tabs. + +```tsx + +``` + + + + + +### Tab Height + + + + + +## Groups + +### 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; +``` + +### Group Controls Panel + +`DockviewReact` accepts a prop `groupControlComponent` which expects a React component whos props are `IDockviewGroupControlProps`. +This control will be rendered inside the header bar on the right hand side for each group of tabs. + +```tsx +const Component: React.FunctionComponent = () => { + return
{'...'}
; +}; + +return ; +``` + +As a simple example the below uses the `groupControlComponent` to render a small control that indicates whether the group +is active and which panel is active in that group. + +```tsx +const GroupControlComponent = (props: IDockviewGroupControlProps) => { + const isGroupActive = props.isGroupActive; + const activePanel = props.activePanel; + + return ( +
+ + {isGroupActive ? 'Group Active' : 'Group Inactive'} + + {`activePanel: ${ + activePanel?.id || 'null' + }`} +
+ ); +}; +``` + + + + + +### Constraints + +You may wish to specify a minimum or maximum height or width for a group which can be done through the group api. + +```tsx +api.group.api.setConstraints(...) +``` + +> Constraints are currently only supported for groups and not individual panels. +> 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. + + + + + +## Events + +A simple example showing events fired by `dockviewz that can be interacted with. + + + + + +## Advanced Examples + +### 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`. + + + + + +### Example + +hello + + + +hello 2 + +
+ +
+ +## VanillaJS + +> Note: This section is experimental and support for Vanilla JS is a work in progress. + +The `dockview` package contains `ReactJS` wrappers for the core library. +The core library is published as an independant package under the name `dockview-core` which you can install standalone. + +> When using `dockview` there is no need to also install `dockview-core`. +> `dockview-core` is a dependency of `dockview` and automatically installed during the installation process of `dockview` via `npm install dockview`. + + diff --git a/packages/docs/versioned_docs/version-1.7.1/components/gridview.mdx b/packages/docs/versioned_docs/version-1.7.1/components/gridview.mdx new file mode 100644 index 000000000..d531f7fdc --- /dev/null +++ b/packages/docs/versioned_docs/version-1.7.1/components/gridview.mdx @@ -0,0 +1,120 @@ +--- +description: Gridview Documentation +--- + +import { SimpleGridview } from '@site/src/components/simpleGridview'; +import { EventsGridview } from '@site/src/components/gridview/events'; +import Link from '@docusaurus/Link'; + +# Gridview + +## Introduction + +
+ +
+ +## GridviewReact Component + +```tsx +import { ReactGridview } from 'dockview'; +``` + +| Property | Type | Optional | Default | Description | +| ------------------- | ------------------------------------ | -------- | ---------------------- | ------------------------------------------------------------------------ | +| onReady | (event: SplitviewReadyEvent) => void | No | | | +| components | object | No | | | +| orientation | Orientation | Yes | Orientation.HORIZONTAL | | +| proportionalLayout | boolean | Yes | true | See Proportional layout | +| hideBorders | boolean | Yes | false | | +| className | string | Yes | '' | | +| disableAutoResizing | boolean | Yes | false | See Auto Resizing | + +## Gridview API + +```tsx +const MyComponent = (props: IGridviewPanelProps<{ title: string }>) => { + // props.containerApi... + + return
{`My first panel has the title: ${props.params.title}`}
; +}; +``` + +```tsx +const onReady = (event: GridviewReadyEvent) => { + // event.api... +}; +``` + +| Property | Type | Description | +| ---------------------- | ------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------- | +| height | `number` | Component pixel height | +| width | `number` | Component pixel width | +| minimumHeight | `number` | | +| maximumHeight | `number` | | +| maximumWidth | `number` | | +| maximumWidth | `number` | | +| length | `number` | Number of panels | +| panels | `ISplitviewPanel[]` | all panels | +| orientation | `Orientation` | | +| | | | +| onDidLayoutChange | `Event` | Fires on layout change | +| onDidLayoutFromJSON | `Event` | Fires of layout change caused by a fromJSON deserialization call | +| onDidAddPanel | `Event` | Fires when a view is added | +| onDidRemovePanel | `Event` | Fires when a view is removed | +| onDidActivePanelChange | `Event` | Fires when the active group changes | +| | | | +| addPanel | `addPanel(options: AddComponentOptions): IGridviewPanel` | | +| removePanel | `(panel: IGridviewPanel, sizing?: Sizing): void` | | +| movePanel | `(panel: IGridviewPanel, options: {direction: Direction, refernece:string, size?: number}): void` | | +| getPanel | `(id: string) \| IGridviewPanel \| undefined` | | +| | | | +| updateOptions | `(options:SplitviewComponentUpdateOptions): void` | | +| focus | `(): void` | Focus the active panel, if exists | +| layout | `(width: number, height:number): void` | Auto Resizing | +| fromJSON | `(data: SerializedGridview): void` | Serialization | +| toJSON | `(): SerializedGridview` | Serialization | +| clear | `(): void` | Clears the current layout | + +## Gridview Panel API + +```tsx +const MyComponent = (props: IGridviewPanelProps<{ title: string }>) => { + // props.api... + + return
{`My first panel has the title: ${props.params.title}`}
; +}; +``` + +| Property | Type | Description | +| ---------------------- | ----------------------------------------------------------- | ---------------- | +| id | `string` | Panel id | +| isFocused | `boolean` | Is panel focsed | +| isActive | `boolean` | Is panel active | +| isVisible | `boolean` | Is panel visible | +| width | `number` | Panel width | +| height | `number` | Panel height | +| | | | +| onDidDimensionsChange | `Event` | | +| onDidFocusChange | `Event` | | +| onDidVisibilityChange | `Event` | | +| onDidActiveChange | `Event` | | +| onDidConstraintsChange | `onDidConstraintsChange: Event` | | +| | | | +| setVisible | `(isVisible: boolean): void` | | +| setActive | `(): void` | | +| setConstraints | `(value: PanelConstraintChangeEvent2): void;` | | +| setSize | `(event: SizeEvent): void` | | + +## Events + +`GridviewReact` exposes a number of events that the developer can listen to and below is a simple example with a log panel showing those events that occur. + + diff --git a/packages/docs/versioned_docs/version-1.7.1/components/paneview.mdx b/packages/docs/versioned_docs/version-1.7.1/components/paneview.mdx new file mode 100644 index 000000000..87efc8eaa --- /dev/null +++ b/packages/docs/versioned_docs/version-1.7.1/components/paneview.mdx @@ -0,0 +1,285 @@ +--- +description: Paneview Documentation +--- + +import { SimplePaneview } from '@site/src/components/simplePaneview'; +import { CustomHeaderPaneview } from '@site/src/components/paneview/customHeader'; +import { DragAndDropPaneview } from '@site/src/components/paneview/dragAndDrop'; +import { SideBySidePaneview } from '@site/src/components/paneview/sideBySide'; +import Link from '@docusaurus/Link'; + +# Paneview + +A paneview is a collapsed collection of vertically stacked panels and panel headers. +The panel header will always remain visible however the panel will only be visible when the panel is expanded. + +:::info + +Paneview panels can be re-ordered by dragging and dropping the panel headers. + +::: + +--- + +# Introduction + +
+ +
+ +```tsx title="Simple Paneview example" +import { + IPaneviewPanelProps, + PaneviewReact, + PaneviewReadyEvent, +} from 'dockview'; + +const components = { + default: (props: IPaneviewPanelProps<{ title: string }>) => { + return ( +
+ {props.params.title} +
+ ); + }, +}; + +SimplePaneview = () => { + const onReady = (event: PaneviewReadyEvent) => { + event.api.addPanel({ + id: 'panel_1', + component: 'default', + params: { + title: 'Panel 1', + }, + title: 'Panel 1', + }); + + event.api.addPanel({ + id: 'panel_2', + component: 'default', + params: { + title: 'Panel 2', + }, + title: 'Panel 2', + }); + + event.api.addPanel({ + id: 'panel_3', + component: 'default', + params: { + title: 'Panel 3', + }, + title: 'Panel 3', + }); + }; + + return ( + + ); +}; +``` + +## PaneviewReact Component + +You can create a Paneview through the use of the `ReactPaneview` component. + +```tsx +import { ReactPaneview } from 'dockview'; +``` + +| Property | Type | Optional | Default | Description | +| ------------------- | ------------------------------------ | -------- | ------- | -------------------------------------------------------- | +| onReady | (event: SplitviewReadyEvent) => void | No | | | +| components | object | No | | | +| headerComponents | object | Yes | | | +| className | string | Yes | '' | | +| disableAutoResizing | boolean | Yes | false | Auto Resizing | +| disableDnd | boolean | Yes | false | | +| onDidDrop | Event | Yes | | | + +## Paneview API + +The Paneview 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="Paneview API via Panel component" +const MyComponent = (props: IGridviewPanelProps<{ title: string }>) => { + // props.containerApi... + + return
{`My first panel has the title: ${props.params.title}`}
; +}; +``` + +```tsx title="Paneview API via the onReady callback" +const onReady = (event: GridviewReadyEvent) => { + // event.api... +}; +``` + +| Property | Type | Description | +| ------------------- | ---------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- | +| height | `number` | Component pixel height | +| width | `number` | Component pixel width | +| minimumSize | `number` | The sum of the `minimumSize` property for each panel | +| maximumSize | `number` | The sum of the `maximumSize` property for each panel | +| length | `number` | Number of panels | +| panels | `IPaneviewPanel[]` | All panels | +| | | | +| onDidLayoutChange | `Event` | Fires on layout change | +| onDidLayoutFromJSON | `Event` | Fires of layout change caused by a fromJSON deserialization call | +| onDidAddView | `Event` | Fires when a view is added | +| onDidRemoveView | `Event` | Fires when a view is removed | +| onDidDrop | `EventDrag and Drop) | +| | | | +| addPanel | `addPanel(options: AddPaneviewComponentOptions): IPaneviewPanel` | | +| removePanel | `(panel: IPaneviewPanel): void` | | +| movePanel | `(from: number, to: number): void` | | +| getPanel | `(id:string): IPaneviewPanel \| undefined` | | +| | | | +| focus | `(): void` | Focus the active panel, if exists | +| layout | `(width: number, height:number): void` | See Auto Resizing | +| fromJSON | `(data: SerializedPaneview): void` | Serialization | +| toJSON | `(): SerializedPaneview` | Serialization | +| clear | `(): void` | Clears the current layout | + +## Paneview Panel API + +```tsx +const MyComponent = (props: IGridviewPanelProps<{ title: string }>) => { + // props.api... + + return
{`My first panel has the title: ${props.params.title}`}
; +}; +``` + +| Property | Type | Description | +| ---------------------- | ----------------------------------------------------------- | ---------------- | +| id | `string` | Panel id | +| isFocused | `boolean` | Is panel focsed | +| isActive | `boolean` | Is panel active | +| isVisible | `boolean` | Is panel visible | +| width | `number` | Panel width | +| height | `number` | Panel height | +| | | +| onDidDimensionsChange | `Event` | | +| onDidFocusChange | `Event` | | +| onDidVisibilityChange | `Event` | | +| onDidActiveChange | `Event` | | +| onDidConstraintsChange | `onDidConstraintsChange: Event` | | +| | | +| setVisible | `(isVisible: boolean): void` | | +| setActive | `(): void` | | +| setConstraints | `(value: PanelConstraintChangeEvent2): void;` | | +| setSize | `(event: SizeEvent): void` | | + +## Advanced Features + +### Custom Header + +You can provide a custom component to render an alternative header. + +
+ +
+ +You can provide a `headerComponent` option when creating a panel to tell the library to use a custom header component. + +```tsx +const onReady = (event: PaneviewReadyEvent) => { + event.api.addPanel({ + id: 'panel_1', + component: 'default', + headerComponent: 'myHeaderComponent', + params: { + valueA: 'A', + }, + title: 'Panel 1', + }); +}; +``` + +This header must be defined in the collection of components provided to the `headerComponents` props for `ReactPaneivew` + +```tsx +import { IPaneviewPanelProps } from 'dockview'; + +const MyHeaderComponent = (props: IPaneviewPanelProps<{ title: string }>) => { + const [expanded, setExpanded] = React.useState( + props.api.isExpanded + ); + + React.useEffect(() => { + const disposable = props.api.onDidExpansionChange((event) => { + setExpanded(event.isExpanded); + }); + + return () => { + disposable.dispose(); + }; + }, []); + + const onClick = () => { + props.api.setExpanded(!expanded); + }; + + return ( + + ); +}; + +const headerComponents = { myHeaderComponent: MyHeaderComponent }; +``` + +### Drag And Drop + +If you provide the `PaneviewReact` component with the prop `onDidDrop` you will be able to interact with custom drop events. + + + +### Interactions + +You can safely create multiple paneview instances within one page. They will not interact with each other by default. + +If you wish to interact with the drop event from one paneview instance in another paneview instance you can implement the `showDndOverlay` and `onDidDrop` props on `PaneviewReact`. + +As an example see how dragging a header from one control to another will only trigger an interactable event for the developer if the checkbox is enabled. + + diff --git a/packages/docs/versioned_docs/version-1.7.1/components/splitview.mdx b/packages/docs/versioned_docs/version-1.7.1/components/splitview.mdx new file mode 100644 index 000000000..a32e6d5a2 --- /dev/null +++ b/packages/docs/versioned_docs/version-1.7.1/components/splitview.mdx @@ -0,0 +1,246 @@ +--- +description: Splitview Documentation +--- + +import { SimpleSplitview } from '@site/src/components/simpleSplitview'; +import { SplitviewExample1 } from '@site/src/components/splitview/active'; +import Link from '@docusaurus/Link'; + +# Splitview + +## Introduction + +A Splitview is a collection of resizable horizontally or vertically stacked panels. + +
+ +
+ +```tsx title="Simple Splitview example" +import { + ISplitviewPanelProps, + Orientation, + SplitviewReact, + SplitviewReadyEvent, +} from 'dockview'; + +const components = { + default: (props: ISplitviewPanelProps<{ title: string }>) => { + return
{props.params.title}
; + }, +}; + +export const SimpleSplitview = () => { + const onReady = (event: SplitviewReadyEvent) => { + event.api.addPanel({ + id: 'panel_1', + component: 'default', + params: { + title: 'Panel 1', + }, + }); + + event.api.addPanel({ + id: 'panel_2', + component: 'default', + params: { + title: 'Panel 2', + }, + }); + + event.api.addPanel({ + id: 'panel_3', + component: 'default', + params: { + title: 'Panel 3', + }, + }); + }; + + return ( + + ); +}; +``` + +## SplitviewReact Component + +You can create a Splitview through the use of the `ReactSplitview` component. + +```tsx +import { ReactSplitview } from 'dockview'; +``` + +Using the `onReady` prop you can access to the component `api` and add panels either through deserialization or the individual addition of panels. + +| Property | Type | Optional | Default | Description | +| ------------------- | -------------------------------------- | -------- | ------------------------ | ------------------------------------------------------------------------ | +| onReady | `(event: SplitviewReadyEvent) => void` | No | | Function | +| components | `Record` | No | | Panel renderers | +| orientation | `Orientation` | Yes | `Orientation.HORIZONTAL` | Orientation of the Splitview | +| proportionalLayout | `boolean` | Yes | `true` | See Proportional layout | +| hideBorders | `boolean` | Yes | `false` | Hide the borders between panels | +| className | `string` | Yes | `''` | Attaches a classname | +| disableAutoResizing | `boolean` | Yes | `false` | See Auto Resizing | + +## Splitview API + +The Splitview 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="Splitview API via Panel component" +const MyComponent = (props: ISplitviewPanelProps<{ title: string }>) => { + // props.containerApi... + + return
{`My first panel has the title: ${props.params.title}`}
; +}; +``` + +```tsx title="Splitview API via the onReady callback" +const onReady = (event: SplitviewReadyEvent) => { + // event.api... +}; +``` + +| Property | Type | Description | +| ------------------- | ------------------------------------------------------------------ | ---------------------------------------------------------------- | +| height | `number` | Component pixel height | +| width | `number` | Component pixel width | +| minimumSize | `number` | The sum of the `minimumSize` property for each panel | +| maximumSize | `number` | The sum of the `maximumSize` property for each panel | +| length | `number` | Number of panels | +| panels | `ISplitviewPanel[]` | All panels | +| | | | +| onDidLayoutChange | `Event` | Fires on layout change | +| onDidLayoutFromJSON | `Event` | Fires of layout change caused by a fromJSON deserialization call | +| onDidAddView | `Event` | Fires when a view is added | +| onDidRemoveView | `Event` | Fires when a view is removed | +| | | | +| addPanel | `addPanel(options: AddSplitviewComponentOptions): ISplitviewPanel` | | +| removePanel | `(panel: ISplitviewPanel, sizing?: Sizing): void` | | +| getPanel | `(id:string): ISplitviewPanel \| undefined` | | +| movePanel | `(from: number, to: number): void` | | +| | | +| updateOptions | `(options: SplitviewComponentUpdateOptions): void` | | +| focus | `(): void` | Focus the active panel, if exists | +| layout | `(width: number, height:number): void` | See Auto Resizing | +| fromJSON | `(data: SerializedSplitview): void` | Serialization | +| toJSON | `(): SerializedSplitview` | Serialization | +| clear | `(): void` | Clears the current layout | + +## Splitview Panel API + +The Splitview panel API is exposed on each panel containing actions and variables specific to that panel. + +```tsx title="Splitview panel API via Panel component" +const MyComponent = (props: ISplitviewPanelProps<{ title: string }>) => { + // props.api... + + return
{`My first panel has the title: ${props.params.title}`}
; +}; +``` + +| Property | Type | Description | +| ---------------------- | ----------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- | +| id | `string` | Panel id | +| isFocused | `boolean` | Is panel focsed | +| isActive | `boolean` | Is panel active | +| isVisible | `boolean` | Is panel visible | +| width | `number` | Panel width | +| height | `number` | Panel height | +| | | | +| onDidDimensionsChange | `Event` | Fires when panel dimensions change | +| onDidFocusChange | `Event` | Fire when panel is focused and blurred | +| onDidVisibilityChange | `Event` | Fires when the panels visiblity property is changed (see Panel Visibility) | +| onDidActiveChange | `Event` | Fires when the panels active property is changed (see Active Panel) | +| onDidConstraintsChange | `onDidConstraintsChange: Event` | Fires when the panels size contrainsts change (see Panel Constraints) | +| | | | +| setVisible | `(isVisible: boolean): void` | | +| setActive | `(): void` | | +| | | | +| setConstraints | `(value: PanelConstraintChangeEvent2): void;` | | +| setSize | `(event: PanelSizeEvent): void` | | + +## Advanced Features + +Listed below are some functionalities avalaible through both the panel and component APIs. The live demo shows examples of these in real-time. + +
+ +
+ +### Visibility + +A panels visibility can be controlled and monitored through the following code. +A panel with visibility set to `false` will remain as a part of the components list of panels but will not be rendered. + +```tsx +const disposable = props.api.onDidVisibilityChange(({ isVisible }) => { + // +}); +``` + +```tsx +api.setVisible(true); +``` + +### Active + +Only one panel in the `splitview` can be the active panel at any one time. +Setting a panel as active will set all the others as inactive. +A focused panel is always the active panel but an active panel is not always focused. + +```tsx +const disposable = props.api.onDidActiveChange(({ isActive }) => { + // +}); +``` + +```tsx +api.setActive(); +``` + +### Contraints + +When adding a panel you can specify pixel size contraints + +```tsx +event.api.addPanel({ + id: 'panel_3', + component: 'default', + minimumSize: 100, + maximumSize: 1000, +}); +``` + +These contraints can be updated throughout the lifecycle of the `splitview` using the panel API + +```tsx +props.api.onDidConstraintsChange(({ maximumSize, minimumSize }) => { + // +}); +``` + +```tsx +api.setConstraints({ + maximumSize: 200, + minimumSize: 400, +}); +``` diff --git a/packages/docs/versioned_docs/version-1.7.1/example.mdx b/packages/docs/versioned_docs/version-1.7.1/example.mdx new file mode 100644 index 000000000..29989cd51 --- /dev/null +++ b/packages/docs/versioned_docs/version-1.7.1/example.mdx @@ -0,0 +1,8 @@ +import Dockview from '@site/sandboxes/advanced-example-dockview/src/app'; +import useBaseUrl from '@docusaurus/useBaseUrl'; + +import { Container } from '@site/src/components/ui/container'; + + + + diff --git a/packages/docs/versioned_docs/version-1.7.1/index.mdx b/packages/docs/versioned_docs/version-1.7.1/index.mdx new file mode 100644 index 000000000..0e96a515f --- /dev/null +++ b/packages/docs/versioned_docs/version-1.7.1/index.mdx @@ -0,0 +1,149 @@ +--- +sidebar_position: 0 +description: A zero dependency layout manager built for React +--- + +import { SimpleSplitview } from '@site/src/components/simpleSplitview'; +import { SimpleGridview } from '@site/src/components/simpleGridview'; +import { SimplePaneview } from '@site/src/components/simplePaneview'; +import SimpleDockview from '@site/sandboxes/simple-dockview/src/app'; + +# Introduction + +**dockview** is a zero dependency layout manager that supports tab, grids and splitviews. + +## Features + +- Themable and customizable +- Support for the serialization and deserialization of layouts +- Drag and drop support + +## Quick start + +`dockview` has a peer dependency on `react >= 16.8.0` and `react-dom >= 16.8.0`. To install `dockview` you can run: + +```shell +npm install dockview +``` + +You must also import the dockview stylesheet found under [`dockview/dict/styles/dockview.css`](https://unpkg.com/browse/dockview@latest/dist/styles/dockview.css), +depending on your solution this might be: + +```css +@import './node_modules/dockview/dist/styles/dockview.css'; +``` + +A dark and light theme are provided, one of these classes (or a custom theme) must be attached at any point above your components in the HTML tree. To cover the entire web page you might attach the class to the `body` component: + +```html + + ... + + + ... + +``` + +There are 4 components you may want to use: + +Splitview + +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +```tsx +import { + DockviewReact, + DockviewReadyEvent, + PanelCollection, + IDockviewPanelProps, + IDockviewPanelHeaderProps, +} from 'dockview'; + +const components: PanelCollection = { + default: (props: IDockviewPanelProps<{ someProps: string }>) => { + return
{props.params.someProps}
; + }, +}; + +const headers: PanelCollection = { + customTab: (props: IDockviewPanelHeaderProps) => { + return ( +
+ {props.api.title} + props.api.close()}>{'[x]'} +
+ ); + }, +}; + +const Component = () => { + const onReady = (event: DockviewReadyEvent) => { + event.api.addPanel({ + id: 'panel1', + component: 'default', + tabComponent: 'customTab', // optional custom header + params: { + someProps: 'Hello', + }, + }); + event.api.addPanel({ + id: 'panel2', + component: 'default', + params: { + someProps: 'World', + }, + position: { referencePanel: 'panel1', direction: 'below' }, + }); + }; + + return ( + + ); +}; +``` diff --git a/packages/docs/versioned_docs/version-1.7.1/theme.mdx b/packages/docs/versioned_docs/version-1.7.1/theme.mdx new file mode 100644 index 000000000..bd770f5d7 --- /dev/null +++ b/packages/docs/versioned_docs/version-1.7.1/theme.mdx @@ -0,0 +1,89 @@ +--- +sidebar_position: 3 +description: Theming Dockview Components +--- + +import { CustomCSSDockview } from '@site/src/components/dockview/customCss'; + +# 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 depending can be imported + +```css +@import './node_modules/dockview/dist/styles/dockview.css'; +``` + +## Provided themes + +The following are provided as classes that you can attached to your components for themeing + +- `.dockview-theme-light` +- `.dockview-theme-dark` +- `.dockview-theme-abyss` + +## 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 | | + +You can further customise the theme through adjusting class properties but this is up you. +As an 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 +.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); + } + } +} +``` + +
+ +
diff --git a/packages/docs/versioned_sidebars/version-1.7.1-sidebars.json b/packages/docs/versioned_sidebars/version-1.7.1-sidebars.json new file mode 100644 index 000000000..caea0c03b --- /dev/null +++ b/packages/docs/versioned_sidebars/version-1.7.1-sidebars.json @@ -0,0 +1,8 @@ +{ + "tutorialSidebar": [ + { + "type": "autogenerated", + "dirName": "." + } + ] +} diff --git a/packages/docs/versions.json b/packages/docs/versions.json index 48883d079..9d56c44cf 100644 --- a/packages/docs/versions.json +++ b/packages/docs/versions.json @@ -1,3 +1,4 @@ [ + "1.7.1", "1.7.0" ] From 99d4c1e180f36b51200fdfc509df7f0670fb3df2 Mon Sep 17 00:00:00 2001 From: mathuo <6710312+mathuo@users.noreply.github.com> Date: Tue, 11 Apr 2023 21:06:17 +0100 Subject: [PATCH 10/22] docs: 1.7.1 --- packages/docs/blog/2023-04-11-dockview-1.7.1.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/docs/blog/2023-04-11-dockview-1.7.1.md b/packages/docs/blog/2023-04-11-dockview-1.7.1.md index 8fb604d55..7c7b0815a 100644 --- a/packages/docs/blog/2023-04-11-dockview-1.7.1.md +++ b/packages/docs/blog/2023-04-11-dockview-1.7.1.md @@ -13,6 +13,8 @@ If you feel anything is missing or unclear please let me know. - Resize observer [#227](https://github.com/mathuo/dockview/pull/227) - Minor type fix [#237](https://github.com/mathuo/dockview/pull/237) +- Fix close button on default watermark [#225](https://github.com/mathuo/dockview/pull/225) +- Fix edge-case bug when dropping a panel on far corners [#243](https://github.com/mathuo/dockview/pull/243) ## 🛠 Miscs @@ -21,6 +23,5 @@ If you feel anything is missing or unclear please let me know. ## 🔥 Breaking changes -- Fix close button on default watermark [#225](https://github.com/mathuo/dockview/pull/225) - Remove tab height control as prop to `DockviewReact` component. Please control via CSS instead, see docs for tab height. [#236](https://github.com/mathuo/dockview/pull/236) -- Fix edge-case bug when dropping a panel on far corners [#243](https://github.com/mathuo/dockview/pull/243) + From 302c26984937a7943e2a5b29a058834350a8fd33 Mon Sep 17 00:00:00 2001 From: mathuo <6710312+mathuo@users.noreply.github.com> Date: Tue, 11 Apr 2023 21:06:54 +0100 Subject: [PATCH 11/22] chore(release): publish v1.7.1 --- lerna.json | 2 +- packages/dockview-core/package.json | 4 ++-- packages/dockview/package.json | 6 +++--- packages/docs/package.json | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/lerna.json b/lerna.json index e1f41352e..6a7a58efb 100644 --- a/lerna.json +++ b/lerna.json @@ -3,7 +3,7 @@ "packages/*" ], "useWorkspaces": true, - "version": "1.7.0", + "version": "1.7.1", "npmClient": "yarn", "command": { "publish": { diff --git a/packages/dockview-core/package.json b/packages/dockview-core/package.json index f429edff8..7714ca455 100644 --- a/packages/dockview-core/package.json +++ b/packages/dockview-core/package.json @@ -1,6 +1,6 @@ { "name": "dockview-core", - "version": "1.7.0", + "version": "1.7.1", "description": "Zero dependency layout manager supporting tabs, grids and splitviews with ReactJS support", "main": "./dist/cjs/index.js", "types": "./dist/cjs/index.d.ts", @@ -65,4 +65,4 @@ "rollup-plugin-postcss": "^4.0.2", "typedoc": "^0.23.25" } -} \ No newline at end of file +} diff --git a/packages/dockview/package.json b/packages/dockview/package.json index a3d232cea..01117bc7f 100644 --- a/packages/dockview/package.json +++ b/packages/dockview/package.json @@ -1,6 +1,6 @@ { "name": "dockview", - "version": "1.7.0", + "version": "1.7.1", "description": "Zero dependency layout manager supporting tabs, grids and splitviews with ReactJS support", "main": "./dist/cjs/index.js", "types": "./dist/cjs/index.d.ts", @@ -56,7 +56,7 @@ "author": "https://github.com/mathuo", "license": "MIT", "dependencies": { - "dockview-core": "^1.7.0" + "dockview-core": "^1.7.1" }, "devDependencies": { "@rollup/plugin-node-resolve": "^15.0.1", @@ -74,4 +74,4 @@ "rollup-plugin-postcss": "^4.0.2", "typedoc": "^0.23.25" } -} \ No newline at end of file +} diff --git a/packages/docs/package.json b/packages/docs/package.json index b13f0c8d5..7e349fc32 100644 --- a/packages/docs/package.json +++ b/packages/docs/package.json @@ -1,6 +1,6 @@ { "name": "dockview-docs", - "version": "1.7.0", + "version": "1.7.1", "private": true, "scripts": { "docusaurus": "docusaurus", @@ -23,7 +23,7 @@ "@minoru/react-dnd-treeview": "^3.4.3", "axios": "^1.3.3", "clsx": "^1.2.1", - "dockview": "^1.7.0", + "dockview": "^1.7.1", "prism-react-renderer": "^1.3.5", "react": "^18.2.0", "react-dnd": "^16.0.1", From 7f54cff960e8b2423ccaaac075984bff9370b1dc Mon Sep 17 00:00:00 2001 From: mathuo <6710312+mathuo@users.noreply.github.com> Date: Tue, 11 Apr 2023 21:22:48 +0100 Subject: [PATCH 12/22] docs: fix website --- packages/docs/docs/basics.mdx | 115 +++ packages/docs/docs/example.mdx | 8 - .../version-1.7.0/components/_category_.json | 9 - .../version-1.7.0/components/dockview.mdx | 726 ------------------ .../version-1.7.0/components/gridview.mdx | 120 --- .../version-1.7.0/components/paneview.mdx | 285 ------- .../version-1.7.0/components/splitview.mdx | 246 ------ .../versioned_docs/version-1.7.0/index.mdx | 149 ---- .../versioned_docs/version-1.7.0/theme.mdx | 89 --- .../versioned_docs/version-1.7.1/basics.mdx | 115 +++ .../versioned_docs/version-1.7.1/example.mdx | 8 - .../version-1.7.0-sidebars.json | 8 - packages/docs/versions.json | 3 +- 13 files changed, 231 insertions(+), 1650 deletions(-) create mode 100644 packages/docs/docs/basics.mdx delete mode 100644 packages/docs/docs/example.mdx delete mode 100644 packages/docs/versioned_docs/version-1.7.0/components/_category_.json delete mode 100644 packages/docs/versioned_docs/version-1.7.0/components/dockview.mdx delete mode 100644 packages/docs/versioned_docs/version-1.7.0/components/gridview.mdx delete mode 100644 packages/docs/versioned_docs/version-1.7.0/components/paneview.mdx delete mode 100644 packages/docs/versioned_docs/version-1.7.0/components/splitview.mdx delete mode 100644 packages/docs/versioned_docs/version-1.7.0/index.mdx delete mode 100644 packages/docs/versioned_docs/version-1.7.0/theme.mdx create mode 100644 packages/docs/versioned_docs/version-1.7.1/basics.mdx delete mode 100644 packages/docs/versioned_docs/version-1.7.1/example.mdx delete mode 100644 packages/docs/versioned_sidebars/version-1.7.0-sidebars.json diff --git a/packages/docs/docs/basics.mdx b/packages/docs/docs/basics.mdx new file mode 100644 index 000000000..33d0270c0 --- /dev/null +++ b/packages/docs/docs/basics.mdx @@ -0,0 +1,115 @@ +--- +sidebar_position: 1 +description: How to get started with Dockview +--- + +import { SimpleSplitview } from '@site/src/components/simpleSplitview'; +import { SimpleSplitview2 } from '@site/src/components/simpleSplitview2'; + +# Basics + +asd +This section will take you through a number of concepts that can be applied to all dockview components. + +## Panels + +The below examples use `ReactSplitview` but the logic holds for `ReactPaneview`, `ReactGridview` and `ReactDockview` using their respective implementations and interfaces. +All components require you to provide an `onReady` prop which you can use to build and control your component. + +### Adding a panel with parameters + +You can pass parameters to a panel through the `params` object + +```tsx +const onReady = (event: SplitviewReadyEvent) => { + event.api.addPanel({ + id: 'panel_1', + component: 'myComponent', + params: { + title: 'My Title', + }, + }); +}; +``` + +and you can access those properties through the `props.params` object. The TypeScript interface accepts an optional generic type `T` that corresponds to the params objects type. + +```tsx +const MyComponent = (props: ISplitviewPanelProps<{ title: string }>) => { + return
{`My first panel has the title: ${props.params.title}`}
; +}; +``` + +## API + +There are two types of API you will interact with using `dockview`. + +- The `panel API` is accessible via `props.api` in user defined panels and via the `.api` variable found on panel instances. This API contains actions and variable related to the the individual panel. +- The `container API` is accessible via `event.api` in the `onReady` events and `props.containerApi` in user defined panels. This API contains actions and variable related to the component as a whole. + +```tsx +const MyComponent = (props: ISplitviewPanelProps<{ title: string }>) => { + React.useEffect(() => { + const disposable = props.api.onDidActiveChange((event) => { + console.log(`is panel active: ${event.isActive}`); + }); + return () => { + disposable.dispose(); // remember to dispose of any subscriptions + }; + }, [props.api]); + const addAnotherPanel = React.useCallback(() => { + props.containerApi.addPanel({ + id: 'another_id', + component: 'anotherComponent', + }); + }, [props.containerApi]); + return ( +
+ {`My first panel has the title: ${props.params.title}`} + +
+ ); +}; +``` + +### Serialization + +All components support `toJSON(): T` which returns a Typed object representation of the components state. This same Typed object can be used to deserialize a view using `fromJSON(object: T): void`. + +## Auto resizing + +`SplitviewReact`, `GridviewReact`, `PaneviewReact` and `DockviewReact` will all automatically resize to fill the size of their parent element. +Internally this is achieved using a [ResizeObserver](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver) which some users may need to polyfill. +You can disable this by settings the `disableAutoResizing` prop to be `true`. + +You can manually resize a component using the API method `layout(width: number, height: number): void`. +An advanced case may use this in conjunction with `disableAutoResizing=true` to allow a parent component to have ultimate control over the dimensions of the component. + +## Events + +Many API properties can be listened on using the `Event` pattern. For example `api.onDidFocusChange(() => {...})`. +You should dispose of any event listeners you create cleaning up any listeners you would have created. + +```tsx +React.useEffect(() => { + const disposable = api.onDidFocusChange(() => { + // write some code + }); + return () => { + disposable.dispose(); + }; +}, []); +``` + +## Proportional layout + +The `proportionalLayout` property indicates the expected behaviour of the component as it's container resizes, should all views resize equally or should just one view expand to fill the new space. `proportionalLayout` can be set as a property on `SplitviewReact` and `GridviewReact` components. +Although not configurable on `DockviewReact` and `PaneviewReact` these both behave as if `proportionalLayout=true` was set for them. + + + + + +## Browser support + +dockview is intended to support all major browsers. Some users may require a polyfill for [ResizeObserver](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver). diff --git a/packages/docs/docs/example.mdx b/packages/docs/docs/example.mdx deleted file mode 100644 index 29989cd51..000000000 --- a/packages/docs/docs/example.mdx +++ /dev/null @@ -1,8 +0,0 @@ -import Dockview from '@site/sandboxes/advanced-example-dockview/src/app'; -import useBaseUrl from '@docusaurus/useBaseUrl'; - -import { Container } from '@site/src/components/ui/container'; - - - - diff --git a/packages/docs/versioned_docs/version-1.7.0/components/_category_.json b/packages/docs/versioned_docs/version-1.7.0/components/_category_.json deleted file mode 100644 index 366718010..000000000 --- a/packages/docs/versioned_docs/version-1.7.0/components/_category_.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "label": "Components", - "collapsible": true, - "collapsed": false, - "link": { - "type": "generated-index", - "title": "Components" - } -} \ No newline at end of file diff --git a/packages/docs/versioned_docs/version-1.7.0/components/dockview.mdx b/packages/docs/versioned_docs/version-1.7.0/components/dockview.mdx deleted file mode 100644 index 4b01b318d..000000000 --- a/packages/docs/versioned_docs/version-1.7.0/components/dockview.mdx +++ /dev/null @@ -1,726 +0,0 @@ ---- -description: Dockview Documentation ---- - -import { Container } from '@site/src/components/ui/container'; - -import Link from '@docusaurus/Link'; -import useBaseUrl from '@docusaurus/useBaseUrl'; - -import DockviewPersistance 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 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/groupcontrol-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'; -import DockviewSetTitle from '@site/sandboxes/updatetitle-dockview/src/app'; -import RenderingDockview from '@site/sandboxes/rendering-dockview/src/app'; -import DockviewExternalDnd from '@site/sandboxes/externaldnd-dockview/src/app'; -import DockviewResizeContainer from '@site/sandboxes/resizecontainer-dockview/src/app'; -import { attach as attachDockviewVanilla } from '@site/sandboxes/vanilla-dockview/src/app'; - -# Dockview - -## Introduction - -Dockview is an abstraction built on top of [Gridviews](./gridview) where each view is a container of many tabbed panels. - - - - - -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. - -## DockviewReact Component - -You can create a Dockview through the use of the `DockviewReact` component. - -```tsx -import { DockviewReact } from 'dockview'; -``` - -| Property | Type | Optional | Default | Description | -| --------------------- | ------------------------------------ | -------- | --------- | ------------------------------------------------------------ | -| onReady | (event: SplitviewReadyEvent) => void | No | | | -| components | object | No | | | -| tabComponents | object | Yes | | | -| watermarkComponent | object | Yes | | | -| hideBorders | boolean | Yes | false | | -| className | string | Yes | '' | | -| disableAutoResizing | boolean | Yes | false | See Auto Resizing | -| onDidDrop | Event | Yes | false | | -| showDndOverlay | Event | Yes | false | | -| defaultTabComponent | object | Yes | | | -| groupControlComponent | object | Yes | | | -| singleTabMode | 'fullwidth' \| 'default' | Yes | 'default' | | - -## 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
{`My first panel has the title: ${props.params.title}`}
; -}; -``` - -```tsx title="Dockview API via the onReady callback" -const onReady = (event: DockviewReadyEvent) => { - // event.api... -}; -``` - -| Property | Type | Description | -| ---------------------- | ---------------------------------------------------- | -------------------------------------------------------- | -| height | `number` | Component pixel height | -| width | `number` | Component pixel width | -| minimumHeight | `number` | | -| maximumHeight | `number` | | -| maximumWidth | `number` | | -| maximumWidth | `number` | | -| length | `number` | Number of panels | -| size | `number` | Number of Groups | -| panels | `IDockviewPanel[]` | | -| groups | `GroupPanel[]` | | -| activePanel | `IDockviewPanel \| undefined` | | -| activeGroup | `IDockviewPanel \| undefined` | | -| | | | -| onDidLayoutChange | `Event` | | -| onDidLayoutFromJSON | `Event` | | -| onDidAddGroup | `Event` | | -| onDidRemoveGroup | `Event` | | -| onDidActiveGroupChange | `Event` | | -| onDidAddPanel | `Event` | | -| onDidRemovePanel | `Event` | | -| onDidActivePanelChange | `Event` | | -| onDidDrop | `EventAuto Resizing | -| fromJSON | `(data: SerializedDockview): void` | Serialization | -| toJSON | `(): SerializedDockview` | Serialization | -| clear | `(): void` | Clears the current layout | - -## Dockview Panel API - -```tsx -const MyComponent = (props: IDockviewPanelProps<{ title: string }>) => { - // props.api... - - return
{`My first panel has the title: ${props.params.title}`}
; -}; -``` - -| Property | Type | Description | -| ---------------------- | ----------------------------------------------------------- | ---------------- | -| id | `string` | Panel id | -| isFocused | `boolean` | Is panel focused | -| isActive | `boolean` | Is panel active | -| width | `number` | Panel width | -| height | `number` | Panel height | -| onDidDimensionsChange | `Event` | | -| onDidFocusChange | `Event` | | -| onDidVisibilityChange | `Event` | | -| onDidActiveChange | `Event` | | -| setActive | `(): void` | | -| | | | -| onDidConstraintsChange | `onDidConstraintsChange: Event` | | -| setConstraints | `(value: PanelConstraintChangeEvent2): void;` | | -| setSize | `(event: SizeEvent): void` | | -| | | | -| group | `GroupPanel | undefined` | -| isGroupActive | `boolean` | | -| title | `string` | | -| suppressClosable | `boolean` | | -| close | `(): void` | | -| setTitle | `(title: string): void` | | - -## Layout Persistance - -Layouts are loaded and saved via to `fromJSON` and `toJSON` methods on the Dockview api. -The api also exposes an event `onDidLayoutChange` you can listen on to determine when the layout has changed. -Below are some snippets showing how you might load from and save to localStorage. - -```tsx title="Saving the layout state to localStorage" -React.useEffect(() => { - if (!api) { - return; - } - - const disposable = api.onDidLayoutChange(() => { - const layout = api.toJSON(); - - localStorage.setItem( - 'dockview_persistance_layout', - JSON.stringify(layout) - ); - }); - - return () => { - disposable.dispose(); - }; -}, [api]); -``` - -```tsx title="Loading a layout from localStorage" -const onReady = (event: DockviewReadyEvent) => { - const layoutString = localStorage.getItem('dockview_persistance_layout'); - - let success = false; - - if (layoutString) { - try { - const layout = JSON.parse(layoutString); - event.api.fromJSON(layout); - success = true; - } catch (err) { - // - } - } - - if (!success) { - // do something if there is no layout or there was a loading error - } -}; -``` - -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. - - - - - -## Resizing - -### 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. - - - - - -### Container Resizing - -The component will automatically resize to it's container. - - - - - -## Watermark - -When the dockview is empty you may want to display some fallback content, this is refered to as the `watermark`. -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. - - - - - -## Drag And Drop - -### Built-in behaviours - -Dockview supports a wide variety of built-in Drag and Drop possibilities. -Below are some examples of the operations you can perform. - - - -> Drag a tab onto another tab to place it inbetween existing tabs. - - - -> Drag a tab to the right of the last tab to place it after the existing tabs. - - - -> Drag a group onto an existing group to merge the two groups. - -
- - -
- -> Drag into the left/right/top/bottom target zone of a panel to create a new group in the selected direction. - -> Drag into the center of a panel to add to that group. - -> Drag to the edge of the dockview component to create a new group on the selected edge. - -### 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 ( - -); -``` - - - - - -### Third Party Dnd Libraries - -To be completed... - - - - - -## Panels - -### Add Panel - -Using the dockview API you can access the `addPanel` method which returns an instance of the created panel. -The minimum method signature is: - -```ts -const panel = api.addPanel({ - id: 'my_unique_panel_id', - component: 'my_component', -}); -``` - -where `id` is the unique id of the panel and `component` is the implenentation which -will be used to render the panel. You will have registered this using the `components` prop of the `DockviewReactComponent` component. - -You can optionally provide a `tabComponent` parameters to the `addPanel` method which will render the tab using a custom renderer. -You will have registered this using the `tabComponents` prop of the `DockviewReactComponent` component. - -```ts -const panel = api.addPanel({ - id: 'my_unique_panel_id', - component: 'my_component', - tabComponent: 'my_tab_component', -}); -``` - -You can pass properties to the panel using the `params` key. -You can update these properties through the panels `api` object and its `updateParameters` method. - -```ts -const panel = api.addPanel({ - id: 'my_unique_panel_id', - component: 'my_component', - params: { - myCustomKey: 'my_custom_value', - }, -}); - -panel.api.updateParameters({ - myCustomKey: 'my_custom_value', - myOtherCustomKey: 'my_other_custom_key', -}); -``` - -> Note `updateParameters` does not accept partial parameter updates, you should call it with the entire set of parameters -> you want the panel to receive. - -Finally `addPanel` accepts a `position` object which tells dockview where to place the panel. - -- This object optionally accepts either a `referencePanel` or `referenceGroup` which can be the associated id as a string - or the panel/group object reference. -- This object accepts a `direction` property which dictates where, - relative to the provided reference the new panel will be placed. - -> If neither a `referencePanel` or `referenceGroup` then the provided `direction` will be treated as absolute. - -> If no `direction` is provided the library will place the new panel in a pre-determined position. - -```ts -const panel = api.addPanel({ - id: 'panel_1', - component: 'default', -}); - -const panel2 = api.addPanel({ - id: 'panel_2', - component: 'default', - position: { - referencePanel: panel1, - direction: 'right', - }, -}); -``` - -### Panel Rendering - -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. - -However 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. - -> For example 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. - -This is the default behaviour to ensure the greatest flexibility for the user but through the panels `props.api` you can listen to the visiblity state of the panel -and write additional logic to optimize your application. - -For example 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 -) { - const HigherOrderComponent = (props: IDockviewPanelProps) => { - const [visible, setVisible] = React.useState( - 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. - - - - - -## Headers - -### Custom Tab Headers - -You can provide custom renderers for your tab headers for maximum customization. -A default implementation of `DockviewDefaultTab` is provided should you only wish to attach minor -changes and events that do not alter the default behaviour, for example to add a custom context menu event -handler. - -```tsx title="Attaching a custom context menu event handlers to a custom header" -import { IDockviewPanelHeaderProps, DockviewDefaultTab } from 'dockview'; - -const MyCustomheader = (props: IDockviewPanelHeaderProps) => { - const onContextMenu = (event: React.MouseEvent) => { - event.preventDefault(); - alert('context menu'); - }; - return ; -}; -``` - -You are also free to define a custom renderer entirely from scratch and not make use of the `DockviewDefaultTab` component. -To use a custom renderer you can must register a collection of tab components. - -```tsx -const tabComponents = { - myCustomHeader: MyCustomHeader, -}; - -return ; -``` - -```tsx -api.addPanel({ - id: 'panel_1', - component: 'default', - tabComponent: 'myCustomHeader', // <-- your registered renderers - title: 'Panel 1', -}); -``` - -You can also override the default tab renderer which will be used when no `tabComponent` is provided to the `addPanel` function. - -```tsx -; -``` - -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. - - - - - -### Default Tab Title - -If you are using the default tab renderer you can set the title of a tab when creating it - -```tsx -api.addPanel({ - id: 'panel_1', - component: 'my_component', - title: 'my_custom_title', // <-- special param for title -}); -``` - -You can update the title through the panel api which can be accessed via `props.api` if you are inside the panel -component or via `api.getPanel('panel1').api` if you are accessing from outside of the panel component. - -```tsx -api.setTitle('my_new_custom_title'); -``` - -> Note this only works when using the default tab implementation. - - - - - -### Custom Tab Title - -If you are using a custom tab implementation you should pass variables through as a parameter and render them -through your tab components implementation. - -```tsx title="Add a panel with custom parameters" -api.addPanel({ - id: 'panel_2', - component: 'my_component', - tabComponent: 'my_tab', - params: { - myTitle: 'Window 2', // <-- passing a variable to use as a title - }, -}); -``` - -```tsx title="Accessing custom parameters from a custom tab renderer" -const tabComponents = { - default: (props: IDockviewPanelHeaderProps<{ myTitle: string }>) => { - const title = props.params.myTitle; // <-- accessing my custom varaible - return
{/** tab implementation as chosen by developer */}
; - }, -}; -``` - -### Hidden Headers - -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; -``` - -### Full width tabs - -`DockviewReactComponent` accepts the prop `singleTabMode`. If set `singleTabMode=fullwidth` then when there is only one tab in a group this tab will expand -to the entire width of the group. For example: - -> This can be conmbined with Locked Groups to create an application that feels more like a Window Manager -> rather than a collection of groups and tabs. - -```tsx - -``` - - - - - -## Groups - -### 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; -``` - -### Group Controls Panel - -`DockviewReact` accepts a prop `groupControlComponent` which expects a React component whos props are `IDockviewGroupControlProps`. -This control will be rendered inside the header bar on the right hand side for each group of tabs. - -```tsx -const Component: React.FunctionComponent = () => { - return
{'...'}
; -}; - -return ; -``` - -As a simple example the below uses the `groupControlComponent` to render a small control that indicates whether the group -is active and which panel is active in that group. - -```tsx -const GroupControlComponent = (props: IDockviewGroupControlProps) => { - const isGroupActive = props.isGroupActive; - const activePanel = props.activePanel; - - return ( -
- - {isGroupActive ? 'Group Active' : 'Group Inactive'} - - {`activePanel: ${ - activePanel?.id || 'null' - }`} -
- ); -}; -``` - - - - - -### Constraints - -You may wish to specify a minimum or maximum height or width for a group which can be done through the group api. - -```tsx -api.group.api.setConstraints(...) -``` - -> Constraints are currently only supported for groups and not individual panels. -> 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. - - - - - -## Events - -A simple example showing events fired by `dockviewz that can be interacted with. - - - - - -## Advanced Examples - -### 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`. - - - - - -### Example - -hello - - - -hello 2 - -
- -
- -## VanillaJS - -> Note: This section is experimental and support for Vanilla JS is a work in progress. - -The `dockview` package contains `ReactJS` wrappers for the core library. -The core library is published as an independant package under the name `dockview-core` which you can install standalone. - -> When using `dockview` there is no need to also install `dockview-core`. -> `dockview-core` is a dependency of `dockview` and automatically installed during the installation process of `dockview` via `npm install dockview`. - - diff --git a/packages/docs/versioned_docs/version-1.7.0/components/gridview.mdx b/packages/docs/versioned_docs/version-1.7.0/components/gridview.mdx deleted file mode 100644 index d531f7fdc..000000000 --- a/packages/docs/versioned_docs/version-1.7.0/components/gridview.mdx +++ /dev/null @@ -1,120 +0,0 @@ ---- -description: Gridview Documentation ---- - -import { SimpleGridview } from '@site/src/components/simpleGridview'; -import { EventsGridview } from '@site/src/components/gridview/events'; -import Link from '@docusaurus/Link'; - -# Gridview - -## Introduction - -
- -
- -## GridviewReact Component - -```tsx -import { ReactGridview } from 'dockview'; -``` - -| Property | Type | Optional | Default | Description | -| ------------------- | ------------------------------------ | -------- | ---------------------- | ------------------------------------------------------------------------ | -| onReady | (event: SplitviewReadyEvent) => void | No | | | -| components | object | No | | | -| orientation | Orientation | Yes | Orientation.HORIZONTAL | | -| proportionalLayout | boolean | Yes | true | See Proportional layout | -| hideBorders | boolean | Yes | false | | -| className | string | Yes | '' | | -| disableAutoResizing | boolean | Yes | false | See Auto Resizing | - -## Gridview API - -```tsx -const MyComponent = (props: IGridviewPanelProps<{ title: string }>) => { - // props.containerApi... - - return
{`My first panel has the title: ${props.params.title}`}
; -}; -``` - -```tsx -const onReady = (event: GridviewReadyEvent) => { - // event.api... -}; -``` - -| Property | Type | Description | -| ---------------------- | ------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------- | -| height | `number` | Component pixel height | -| width | `number` | Component pixel width | -| minimumHeight | `number` | | -| maximumHeight | `number` | | -| maximumWidth | `number` | | -| maximumWidth | `number` | | -| length | `number` | Number of panels | -| panels | `ISplitviewPanel[]` | all panels | -| orientation | `Orientation` | | -| | | | -| onDidLayoutChange | `Event` | Fires on layout change | -| onDidLayoutFromJSON | `Event` | Fires of layout change caused by a fromJSON deserialization call | -| onDidAddPanel | `Event` | Fires when a view is added | -| onDidRemovePanel | `Event` | Fires when a view is removed | -| onDidActivePanelChange | `Event` | Fires when the active group changes | -| | | | -| addPanel | `addPanel(options: AddComponentOptions): IGridviewPanel` | | -| removePanel | `(panel: IGridviewPanel, sizing?: Sizing): void` | | -| movePanel | `(panel: IGridviewPanel, options: {direction: Direction, refernece:string, size?: number}): void` | | -| getPanel | `(id: string) \| IGridviewPanel \| undefined` | | -| | | | -| updateOptions | `(options:SplitviewComponentUpdateOptions): void` | | -| focus | `(): void` | Focus the active panel, if exists | -| layout | `(width: number, height:number): void` | Auto Resizing | -| fromJSON | `(data: SerializedGridview): void` | Serialization | -| toJSON | `(): SerializedGridview` | Serialization | -| clear | `(): void` | Clears the current layout | - -## Gridview Panel API - -```tsx -const MyComponent = (props: IGridviewPanelProps<{ title: string }>) => { - // props.api... - - return
{`My first panel has the title: ${props.params.title}`}
; -}; -``` - -| Property | Type | Description | -| ---------------------- | ----------------------------------------------------------- | ---------------- | -| id | `string` | Panel id | -| isFocused | `boolean` | Is panel focsed | -| isActive | `boolean` | Is panel active | -| isVisible | `boolean` | Is panel visible | -| width | `number` | Panel width | -| height | `number` | Panel height | -| | | | -| onDidDimensionsChange | `Event` | | -| onDidFocusChange | `Event` | | -| onDidVisibilityChange | `Event` | | -| onDidActiveChange | `Event` | | -| onDidConstraintsChange | `onDidConstraintsChange: Event` | | -| | | | -| setVisible | `(isVisible: boolean): void` | | -| setActive | `(): void` | | -| setConstraints | `(value: PanelConstraintChangeEvent2): void;` | | -| setSize | `(event: SizeEvent): void` | | - -## Events - -`GridviewReact` exposes a number of events that the developer can listen to and below is a simple example with a log panel showing those events that occur. - - diff --git a/packages/docs/versioned_docs/version-1.7.0/components/paneview.mdx b/packages/docs/versioned_docs/version-1.7.0/components/paneview.mdx deleted file mode 100644 index 87efc8eaa..000000000 --- a/packages/docs/versioned_docs/version-1.7.0/components/paneview.mdx +++ /dev/null @@ -1,285 +0,0 @@ ---- -description: Paneview Documentation ---- - -import { SimplePaneview } from '@site/src/components/simplePaneview'; -import { CustomHeaderPaneview } from '@site/src/components/paneview/customHeader'; -import { DragAndDropPaneview } from '@site/src/components/paneview/dragAndDrop'; -import { SideBySidePaneview } from '@site/src/components/paneview/sideBySide'; -import Link from '@docusaurus/Link'; - -# Paneview - -A paneview is a collapsed collection of vertically stacked panels and panel headers. -The panel header will always remain visible however the panel will only be visible when the panel is expanded. - -:::info - -Paneview panels can be re-ordered by dragging and dropping the panel headers. - -::: - ---- - -# Introduction - -
- -
- -```tsx title="Simple Paneview example" -import { - IPaneviewPanelProps, - PaneviewReact, - PaneviewReadyEvent, -} from 'dockview'; - -const components = { - default: (props: IPaneviewPanelProps<{ title: string }>) => { - return ( -
- {props.params.title} -
- ); - }, -}; - -SimplePaneview = () => { - const onReady = (event: PaneviewReadyEvent) => { - event.api.addPanel({ - id: 'panel_1', - component: 'default', - params: { - title: 'Panel 1', - }, - title: 'Panel 1', - }); - - event.api.addPanel({ - id: 'panel_2', - component: 'default', - params: { - title: 'Panel 2', - }, - title: 'Panel 2', - }); - - event.api.addPanel({ - id: 'panel_3', - component: 'default', - params: { - title: 'Panel 3', - }, - title: 'Panel 3', - }); - }; - - return ( - - ); -}; -``` - -## PaneviewReact Component - -You can create a Paneview through the use of the `ReactPaneview` component. - -```tsx -import { ReactPaneview } from 'dockview'; -``` - -| Property | Type | Optional | Default | Description | -| ------------------- | ------------------------------------ | -------- | ------- | -------------------------------------------------------- | -| onReady | (event: SplitviewReadyEvent) => void | No | | | -| components | object | No | | | -| headerComponents | object | Yes | | | -| className | string | Yes | '' | | -| disableAutoResizing | boolean | Yes | false | Auto Resizing | -| disableDnd | boolean | Yes | false | | -| onDidDrop | Event | Yes | | | - -## Paneview API - -The Paneview 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="Paneview API via Panel component" -const MyComponent = (props: IGridviewPanelProps<{ title: string }>) => { - // props.containerApi... - - return
{`My first panel has the title: ${props.params.title}`}
; -}; -``` - -```tsx title="Paneview API via the onReady callback" -const onReady = (event: GridviewReadyEvent) => { - // event.api... -}; -``` - -| Property | Type | Description | -| ------------------- | ---------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- | -| height | `number` | Component pixel height | -| width | `number` | Component pixel width | -| minimumSize | `number` | The sum of the `minimumSize` property for each panel | -| maximumSize | `number` | The sum of the `maximumSize` property for each panel | -| length | `number` | Number of panels | -| panels | `IPaneviewPanel[]` | All panels | -| | | | -| onDidLayoutChange | `Event` | Fires on layout change | -| onDidLayoutFromJSON | `Event` | Fires of layout change caused by a fromJSON deserialization call | -| onDidAddView | `Event` | Fires when a view is added | -| onDidRemoveView | `Event` | Fires when a view is removed | -| onDidDrop | `EventDrag and Drop) | -| | | | -| addPanel | `addPanel(options: AddPaneviewComponentOptions): IPaneviewPanel` | | -| removePanel | `(panel: IPaneviewPanel): void` | | -| movePanel | `(from: number, to: number): void` | | -| getPanel | `(id:string): IPaneviewPanel \| undefined` | | -| | | | -| focus | `(): void` | Focus the active panel, if exists | -| layout | `(width: number, height:number): void` | See Auto Resizing | -| fromJSON | `(data: SerializedPaneview): void` | Serialization | -| toJSON | `(): SerializedPaneview` | Serialization | -| clear | `(): void` | Clears the current layout | - -## Paneview Panel API - -```tsx -const MyComponent = (props: IGridviewPanelProps<{ title: string }>) => { - // props.api... - - return
{`My first panel has the title: ${props.params.title}`}
; -}; -``` - -| Property | Type | Description | -| ---------------------- | ----------------------------------------------------------- | ---------------- | -| id | `string` | Panel id | -| isFocused | `boolean` | Is panel focsed | -| isActive | `boolean` | Is panel active | -| isVisible | `boolean` | Is panel visible | -| width | `number` | Panel width | -| height | `number` | Panel height | -| | | -| onDidDimensionsChange | `Event` | | -| onDidFocusChange | `Event` | | -| onDidVisibilityChange | `Event` | | -| onDidActiveChange | `Event` | | -| onDidConstraintsChange | `onDidConstraintsChange: Event` | | -| | | -| setVisible | `(isVisible: boolean): void` | | -| setActive | `(): void` | | -| setConstraints | `(value: PanelConstraintChangeEvent2): void;` | | -| setSize | `(event: SizeEvent): void` | | - -## Advanced Features - -### Custom Header - -You can provide a custom component to render an alternative header. - -
- -
- -You can provide a `headerComponent` option when creating a panel to tell the library to use a custom header component. - -```tsx -const onReady = (event: PaneviewReadyEvent) => { - event.api.addPanel({ - id: 'panel_1', - component: 'default', - headerComponent: 'myHeaderComponent', - params: { - valueA: 'A', - }, - title: 'Panel 1', - }); -}; -``` - -This header must be defined in the collection of components provided to the `headerComponents` props for `ReactPaneivew` - -```tsx -import { IPaneviewPanelProps } from 'dockview'; - -const MyHeaderComponent = (props: IPaneviewPanelProps<{ title: string }>) => { - const [expanded, setExpanded] = React.useState( - props.api.isExpanded - ); - - React.useEffect(() => { - const disposable = props.api.onDidExpansionChange((event) => { - setExpanded(event.isExpanded); - }); - - return () => { - disposable.dispose(); - }; - }, []); - - const onClick = () => { - props.api.setExpanded(!expanded); - }; - - return ( -
- ); -}; - -const headerComponents = { myHeaderComponent: MyHeaderComponent }; -``` - -### Drag And Drop - -If you provide the `PaneviewReact` component with the prop `onDidDrop` you will be able to interact with custom drop events. - - - -### Interactions - -You can safely create multiple paneview instances within one page. They will not interact with each other by default. - -If you wish to interact with the drop event from one paneview instance in another paneview instance you can implement the `showDndOverlay` and `onDidDrop` props on `PaneviewReact`. - -As an example see how dragging a header from one control to another will only trigger an interactable event for the developer if the checkbox is enabled. - - diff --git a/packages/docs/versioned_docs/version-1.7.0/components/splitview.mdx b/packages/docs/versioned_docs/version-1.7.0/components/splitview.mdx deleted file mode 100644 index a32e6d5a2..000000000 --- a/packages/docs/versioned_docs/version-1.7.0/components/splitview.mdx +++ /dev/null @@ -1,246 +0,0 @@ ---- -description: Splitview Documentation ---- - -import { SimpleSplitview } from '@site/src/components/simpleSplitview'; -import { SplitviewExample1 } from '@site/src/components/splitview/active'; -import Link from '@docusaurus/Link'; - -# Splitview - -## Introduction - -A Splitview is a collection of resizable horizontally or vertically stacked panels. - -
- -
- -```tsx title="Simple Splitview example" -import { - ISplitviewPanelProps, - Orientation, - SplitviewReact, - SplitviewReadyEvent, -} from 'dockview'; - -const components = { - default: (props: ISplitviewPanelProps<{ title: string }>) => { - return
{props.params.title}
; - }, -}; - -export const SimpleSplitview = () => { - const onReady = (event: SplitviewReadyEvent) => { - event.api.addPanel({ - id: 'panel_1', - component: 'default', - params: { - title: 'Panel 1', - }, - }); - - event.api.addPanel({ - id: 'panel_2', - component: 'default', - params: { - title: 'Panel 2', - }, - }); - - event.api.addPanel({ - id: 'panel_3', - component: 'default', - params: { - title: 'Panel 3', - }, - }); - }; - - return ( - - ); -}; -``` - -## SplitviewReact Component - -You can create a Splitview through the use of the `ReactSplitview` component. - -```tsx -import { ReactSplitview } from 'dockview'; -``` - -Using the `onReady` prop you can access to the component `api` and add panels either through deserialization or the individual addition of panels. - -| Property | Type | Optional | Default | Description | -| ------------------- | -------------------------------------- | -------- | ------------------------ | ------------------------------------------------------------------------ | -| onReady | `(event: SplitviewReadyEvent) => void` | No | | Function | -| components | `Record` | No | | Panel renderers | -| orientation | `Orientation` | Yes | `Orientation.HORIZONTAL` | Orientation of the Splitview | -| proportionalLayout | `boolean` | Yes | `true` | See Proportional layout | -| hideBorders | `boolean` | Yes | `false` | Hide the borders between panels | -| className | `string` | Yes | `''` | Attaches a classname | -| disableAutoResizing | `boolean` | Yes | `false` | See Auto Resizing | - -## Splitview API - -The Splitview 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="Splitview API via Panel component" -const MyComponent = (props: ISplitviewPanelProps<{ title: string }>) => { - // props.containerApi... - - return
{`My first panel has the title: ${props.params.title}`}
; -}; -``` - -```tsx title="Splitview API via the onReady callback" -const onReady = (event: SplitviewReadyEvent) => { - // event.api... -}; -``` - -| Property | Type | Description | -| ------------------- | ------------------------------------------------------------------ | ---------------------------------------------------------------- | -| height | `number` | Component pixel height | -| width | `number` | Component pixel width | -| minimumSize | `number` | The sum of the `minimumSize` property for each panel | -| maximumSize | `number` | The sum of the `maximumSize` property for each panel | -| length | `number` | Number of panels | -| panels | `ISplitviewPanel[]` | All panels | -| | | | -| onDidLayoutChange | `Event` | Fires on layout change | -| onDidLayoutFromJSON | `Event` | Fires of layout change caused by a fromJSON deserialization call | -| onDidAddView | `Event` | Fires when a view is added | -| onDidRemoveView | `Event` | Fires when a view is removed | -| | | | -| addPanel | `addPanel(options: AddSplitviewComponentOptions): ISplitviewPanel` | | -| removePanel | `(panel: ISplitviewPanel, sizing?: Sizing): void` | | -| getPanel | `(id:string): ISplitviewPanel \| undefined` | | -| movePanel | `(from: number, to: number): void` | | -| | | -| updateOptions | `(options: SplitviewComponentUpdateOptions): void` | | -| focus | `(): void` | Focus the active panel, if exists | -| layout | `(width: number, height:number): void` | See Auto Resizing | -| fromJSON | `(data: SerializedSplitview): void` | Serialization | -| toJSON | `(): SerializedSplitview` | Serialization | -| clear | `(): void` | Clears the current layout | - -## Splitview Panel API - -The Splitview panel API is exposed on each panel containing actions and variables specific to that panel. - -```tsx title="Splitview panel API via Panel component" -const MyComponent = (props: ISplitviewPanelProps<{ title: string }>) => { - // props.api... - - return
{`My first panel has the title: ${props.params.title}`}
; -}; -``` - -| Property | Type | Description | -| ---------------------- | ----------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- | -| id | `string` | Panel id | -| isFocused | `boolean` | Is panel focsed | -| isActive | `boolean` | Is panel active | -| isVisible | `boolean` | Is panel visible | -| width | `number` | Panel width | -| height | `number` | Panel height | -| | | | -| onDidDimensionsChange | `Event` | Fires when panel dimensions change | -| onDidFocusChange | `Event` | Fire when panel is focused and blurred | -| onDidVisibilityChange | `Event` | Fires when the panels visiblity property is changed (see Panel Visibility) | -| onDidActiveChange | `Event` | Fires when the panels active property is changed (see Active Panel) | -| onDidConstraintsChange | `onDidConstraintsChange: Event` | Fires when the panels size contrainsts change (see Panel Constraints) | -| | | | -| setVisible | `(isVisible: boolean): void` | | -| setActive | `(): void` | | -| | | | -| setConstraints | `(value: PanelConstraintChangeEvent2): void;` | | -| setSize | `(event: PanelSizeEvent): void` | | - -## Advanced Features - -Listed below are some functionalities avalaible through both the panel and component APIs. The live demo shows examples of these in real-time. - -
- -
- -### Visibility - -A panels visibility can be controlled and monitored through the following code. -A panel with visibility set to `false` will remain as a part of the components list of panels but will not be rendered. - -```tsx -const disposable = props.api.onDidVisibilityChange(({ isVisible }) => { - // -}); -``` - -```tsx -api.setVisible(true); -``` - -### Active - -Only one panel in the `splitview` can be the active panel at any one time. -Setting a panel as active will set all the others as inactive. -A focused panel is always the active panel but an active panel is not always focused. - -```tsx -const disposable = props.api.onDidActiveChange(({ isActive }) => { - // -}); -``` - -```tsx -api.setActive(); -``` - -### Contraints - -When adding a panel you can specify pixel size contraints - -```tsx -event.api.addPanel({ - id: 'panel_3', - component: 'default', - minimumSize: 100, - maximumSize: 1000, -}); -``` - -These contraints can be updated throughout the lifecycle of the `splitview` using the panel API - -```tsx -props.api.onDidConstraintsChange(({ maximumSize, minimumSize }) => { - // -}); -``` - -```tsx -api.setConstraints({ - maximumSize: 200, - minimumSize: 400, -}); -``` diff --git a/packages/docs/versioned_docs/version-1.7.0/index.mdx b/packages/docs/versioned_docs/version-1.7.0/index.mdx deleted file mode 100644 index 0e96a515f..000000000 --- a/packages/docs/versioned_docs/version-1.7.0/index.mdx +++ /dev/null @@ -1,149 +0,0 @@ ---- -sidebar_position: 0 -description: A zero dependency layout manager built for React ---- - -import { SimpleSplitview } from '@site/src/components/simpleSplitview'; -import { SimpleGridview } from '@site/src/components/simpleGridview'; -import { SimplePaneview } from '@site/src/components/simplePaneview'; -import SimpleDockview from '@site/sandboxes/simple-dockview/src/app'; - -# Introduction - -**dockview** is a zero dependency layout manager that supports tab, grids and splitviews. - -## Features - -- Themable and customizable -- Support for the serialization and deserialization of layouts -- Drag and drop support - -## Quick start - -`dockview` has a peer dependency on `react >= 16.8.0` and `react-dom >= 16.8.0`. To install `dockview` you can run: - -```shell -npm install dockview -``` - -You must also import the dockview stylesheet found under [`dockview/dict/styles/dockview.css`](https://unpkg.com/browse/dockview@latest/dist/styles/dockview.css), -depending on your solution this might be: - -```css -@import './node_modules/dockview/dist/styles/dockview.css'; -``` - -A dark and light theme are provided, one of these classes (or a custom theme) must be attached at any point above your components in the HTML tree. To cover the entire web page you might attach the class to the `body` component: - -```html - - ... - - - ... - -``` - -There are 4 components you may want to use: - -Splitview - -
- -
- -
- -
- -
- -
- -
- -
- -```tsx -import { - DockviewReact, - DockviewReadyEvent, - PanelCollection, - IDockviewPanelProps, - IDockviewPanelHeaderProps, -} from 'dockview'; - -const components: PanelCollection = { - default: (props: IDockviewPanelProps<{ someProps: string }>) => { - return
{props.params.someProps}
; - }, -}; - -const headers: PanelCollection = { - customTab: (props: IDockviewPanelHeaderProps) => { - return ( -
- {props.api.title} - props.api.close()}>{'[x]'} -
- ); - }, -}; - -const Component = () => { - const onReady = (event: DockviewReadyEvent) => { - event.api.addPanel({ - id: 'panel1', - component: 'default', - tabComponent: 'customTab', // optional custom header - params: { - someProps: 'Hello', - }, - }); - event.api.addPanel({ - id: 'panel2', - component: 'default', - params: { - someProps: 'World', - }, - position: { referencePanel: 'panel1', direction: 'below' }, - }); - }; - - return ( - - ); -}; -``` diff --git a/packages/docs/versioned_docs/version-1.7.0/theme.mdx b/packages/docs/versioned_docs/version-1.7.0/theme.mdx deleted file mode 100644 index bd770f5d7..000000000 --- a/packages/docs/versioned_docs/version-1.7.0/theme.mdx +++ /dev/null @@ -1,89 +0,0 @@ ---- -sidebar_position: 3 -description: Theming Dockview Components ---- - -import { CustomCSSDockview } from '@site/src/components/dockview/customCss'; - -# 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 depending can be imported - -```css -@import './node_modules/dockview/dist/styles/dockview.css'; -``` - -## Provided themes - -The following are provided as classes that you can attached to your components for themeing - -- `.dockview-theme-light` -- `.dockview-theme-dark` -- `.dockview-theme-abyss` - -## 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 | | - -You can further customise the theme through adjusting class properties but this is up you. -As an 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 -.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); - } - } -} -``` - -
- -
diff --git a/packages/docs/versioned_docs/version-1.7.1/basics.mdx b/packages/docs/versioned_docs/version-1.7.1/basics.mdx new file mode 100644 index 000000000..33d0270c0 --- /dev/null +++ b/packages/docs/versioned_docs/version-1.7.1/basics.mdx @@ -0,0 +1,115 @@ +--- +sidebar_position: 1 +description: How to get started with Dockview +--- + +import { SimpleSplitview } from '@site/src/components/simpleSplitview'; +import { SimpleSplitview2 } from '@site/src/components/simpleSplitview2'; + +# Basics + +asd +This section will take you through a number of concepts that can be applied to all dockview components. + +## Panels + +The below examples use `ReactSplitview` but the logic holds for `ReactPaneview`, `ReactGridview` and `ReactDockview` using their respective implementations and interfaces. +All components require you to provide an `onReady` prop which you can use to build and control your component. + +### Adding a panel with parameters + +You can pass parameters to a panel through the `params` object + +```tsx +const onReady = (event: SplitviewReadyEvent) => { + event.api.addPanel({ + id: 'panel_1', + component: 'myComponent', + params: { + title: 'My Title', + }, + }); +}; +``` + +and you can access those properties through the `props.params` object. The TypeScript interface accepts an optional generic type `T` that corresponds to the params objects type. + +```tsx +const MyComponent = (props: ISplitviewPanelProps<{ title: string }>) => { + return
{`My first panel has the title: ${props.params.title}`}
; +}; +``` + +## API + +There are two types of API you will interact with using `dockview`. + +- The `panel API` is accessible via `props.api` in user defined panels and via the `.api` variable found on panel instances. This API contains actions and variable related to the the individual panel. +- The `container API` is accessible via `event.api` in the `onReady` events and `props.containerApi` in user defined panels. This API contains actions and variable related to the component as a whole. + +```tsx +const MyComponent = (props: ISplitviewPanelProps<{ title: string }>) => { + React.useEffect(() => { + const disposable = props.api.onDidActiveChange((event) => { + console.log(`is panel active: ${event.isActive}`); + }); + return () => { + disposable.dispose(); // remember to dispose of any subscriptions + }; + }, [props.api]); + const addAnotherPanel = React.useCallback(() => { + props.containerApi.addPanel({ + id: 'another_id', + component: 'anotherComponent', + }); + }, [props.containerApi]); + return ( +
+ {`My first panel has the title: ${props.params.title}`} + +
+ ); +}; +``` + +### Serialization + +All components support `toJSON(): T` which returns a Typed object representation of the components state. This same Typed object can be used to deserialize a view using `fromJSON(object: T): void`. + +## Auto resizing + +`SplitviewReact`, `GridviewReact`, `PaneviewReact` and `DockviewReact` will all automatically resize to fill the size of their parent element. +Internally this is achieved using a [ResizeObserver](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver) which some users may need to polyfill. +You can disable this by settings the `disableAutoResizing` prop to be `true`. + +You can manually resize a component using the API method `layout(width: number, height: number): void`. +An advanced case may use this in conjunction with `disableAutoResizing=true` to allow a parent component to have ultimate control over the dimensions of the component. + +## Events + +Many API properties can be listened on using the `Event` pattern. For example `api.onDidFocusChange(() => {...})`. +You should dispose of any event listeners you create cleaning up any listeners you would have created. + +```tsx +React.useEffect(() => { + const disposable = api.onDidFocusChange(() => { + // write some code + }); + return () => { + disposable.dispose(); + }; +}, []); +``` + +## Proportional layout + +The `proportionalLayout` property indicates the expected behaviour of the component as it's container resizes, should all views resize equally or should just one view expand to fill the new space. `proportionalLayout` can be set as a property on `SplitviewReact` and `GridviewReact` components. +Although not configurable on `DockviewReact` and `PaneviewReact` these both behave as if `proportionalLayout=true` was set for them. + + + + + +## Browser support + +dockview is intended to support all major browsers. Some users may require a polyfill for [ResizeObserver](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver). diff --git a/packages/docs/versioned_docs/version-1.7.1/example.mdx b/packages/docs/versioned_docs/version-1.7.1/example.mdx deleted file mode 100644 index 29989cd51..000000000 --- a/packages/docs/versioned_docs/version-1.7.1/example.mdx +++ /dev/null @@ -1,8 +0,0 @@ -import Dockview from '@site/sandboxes/advanced-example-dockview/src/app'; -import useBaseUrl from '@docusaurus/useBaseUrl'; - -import { Container } from '@site/src/components/ui/container'; - - - - diff --git a/packages/docs/versioned_sidebars/version-1.7.0-sidebars.json b/packages/docs/versioned_sidebars/version-1.7.0-sidebars.json deleted file mode 100644 index caea0c03b..000000000 --- a/packages/docs/versioned_sidebars/version-1.7.0-sidebars.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "tutorialSidebar": [ - { - "type": "autogenerated", - "dirName": "." - } - ] -} diff --git a/packages/docs/versions.json b/packages/docs/versions.json index 9d56c44cf..cc477f45b 100644 --- a/packages/docs/versions.json +++ b/packages/docs/versions.json @@ -1,4 +1,3 @@ [ - "1.7.1", - "1.7.0" + "1.7.1" ] From 575a1d7031177b01594efd12a346e37c7770c105 Mon Sep 17 00:00:00 2001 From: mathuo <6710312+mathuo@users.noreply.github.com> Date: Sun, 16 Apr 2023 21:53:28 +0100 Subject: [PATCH 13/22] test: remove smells and test --- .../dockview/dockviewPanelModel.spec.ts | 258 +++++++++++++----- .../src/dockview/deserializer.ts | 30 +- .../src/dockview/dockviewPanelModel.ts | 32 ++- 3 files changed, 218 insertions(+), 102 deletions(-) diff --git a/packages/dockview-core/src/__tests__/dockview/dockviewPanelModel.spec.ts b/packages/dockview-core/src/__tests__/dockview/dockviewPanelModel.spec.ts index b1cf604b0..aacf0ca93 100644 --- a/packages/dockview-core/src/__tests__/dockview/dockviewPanelModel.spec.ts +++ b/packages/dockview-core/src/__tests__/dockview/dockviewPanelModel.spec.ts @@ -4,27 +4,39 @@ import { } from '../../dockview/dockviewComponent'; import { DockviewPanelModel } from '../../dockview/dockviewPanelModel'; import { IContentRenderer, ITabRenderer } from '../../dockview/types'; +import { GroupPanelFrameworkComponentFactory } from '../../dockview/options'; +import { DefaultTab } from '../../dockview/components/tab/defaultTab'; describe('dockviewGroupPanel', () => { - test('that dispose is called on content and tab renderers when present', () => { - const contentMock = jest.fn(() => { + let contentMock: jest.Mock; + let tabMock: jest.Mock; + let accessorMock: jest.Mock; + + beforeEach(() => { + contentMock = jest.fn(() => { const partial: Partial = { element: document.createElement('div'), dispose: jest.fn(), + update: jest.fn(), + onGroupChange: jest.fn(), + onPanelVisibleChange: jest.fn(), }; return partial as IContentRenderer; }); - const tabMock = jest.fn(() => { + tabMock = jest.fn(() => { const partial: Partial = { element: document.createElement('div'), dispose: jest.fn(), + update: jest.fn(), + onGroupChange: jest.fn(), + onPanelVisibleChange: jest.fn(), }; return partial as IContentRenderer; }); - const accessorMock = jest.fn, []>(() => { - return { + accessorMock = jest.fn(() => { + const partial: Partial = { options: { components: { contentComponent: contentMock, @@ -34,8 +46,12 @@ describe('dockviewGroupPanel', () => { }, }, }; - }); + return partial as DockviewComponent; + }); + }); + + test('that dispose is called on content and tab renderers when present', () => { const cut = new DockviewPanelModel( new accessorMock(), 'id', @@ -50,34 +66,6 @@ describe('dockviewGroupPanel', () => { }); test('that update is called on content and tab renderers when present', () => { - const contentMock = jest.fn(() => { - const partial: Partial = { - element: document.createElement('div'), - update: jest.fn(), - }; - return partial as IContentRenderer; - }); - - const tabMock = jest.fn(() => { - const partial: Partial = { - element: document.createElement('div'), - update: jest.fn(), - }; - return partial as IContentRenderer; - }); - - const accessorMock = jest.fn, []>(() => { - return { - options: { - components: { - contentComponent: contentMock, - }, - tabComponents: { - tabComponent: tabMock, - }, - }, - }; - }); const cut = new DockviewPanelModel( new accessorMock(), 'id', @@ -94,36 +82,6 @@ describe('dockviewGroupPanel', () => { }); test('that events are fired', () => { - const contentMock = jest.fn(() => { - const partial: Partial = { - element: document.createElement('div'), - onGroupChange: jest.fn(), - onPanelVisibleChange: jest.fn(), - }; - return partial as IContentRenderer; - }); - - const tabMock = jest.fn(() => { - const partial: Partial = { - element: document.createElement('div'), - onGroupChange: jest.fn(), - onPanelVisibleChange: jest.fn(), - }; - return partial as IContentRenderer; - }); - - const accessorMock = jest.fn, []>(() => { - return { - options: { - components: { - contentComponent: contentMock, - }, - tabComponents: { - tabComponent: tabMock, - }, - }, - }; - }); const cut = new DockviewPanelModel( new accessorMock(), 'id', @@ -168,4 +126,176 @@ describe('dockviewGroupPanel', () => { expect(cut.content.onPanelVisibleChange).toHaveBeenCalledTimes(2); expect(cut.tab.onPanelVisibleChange).toHaveBeenCalledTimes(2); }); + + test('that the default tab is created', () => { + accessorMock = jest.fn(() => { + const partial: Partial = { + options: { + components: { + contentComponent: contentMock, + }, + tabComponents: { + tabComponent: jest + .fn() + .mockImplementation(() => tabMock), + }, + }, + }; + + return partial as DockviewComponent; + }); + + const cut = new DockviewPanelModel( + new accessorMock(), + 'id', + 'contentComponent', + 'tabComponent' + ); + + expect(cut.tab).toEqual(tabMock); + }); + + test('that the provided default tab is chosen when no implementation is provided', () => { + accessorMock = jest.fn(() => { + const partial: Partial = { + options: { + components: { + contentComponent: contentMock, + }, + tabComponents: { + tabComponent: jest + .fn() + .mockImplementation(() => tabMock), + }, + defaultTabComponent: 'tabComponent', + }, + }; + + return partial as DockviewComponent; + }); + + const cut = new DockviewPanelModel( + new accessorMock(), + 'id', + 'contentComponent' + ); + + expect(cut.tab).toEqual(tabMock); + }); + + test('that the framework tab is created when provided tab is a framework tab', () => { + const tab = jest.fn(); + const tabFactory = jest.fn().mockImplementation(() => tab); + + accessorMock = jest.fn(() => { + const partial: Partial = { + options: { + components: { + contentComponent: contentMock, + }, + frameworkTabComponents: { + tabComponent: tabMock, + }, + frameworkComponentFactory: (< + Partial + >{ + tab: { createComponent: tabFactory }, + }) as GroupPanelFrameworkComponentFactory, + }, + }; + + return partial as DockviewComponent; + }); + + const cut = new DockviewPanelModel( + new accessorMock(), + 'id', + 'contentComponent', + 'tabComponent' + ); + + expect(tabFactory).toHaveBeenCalledWith('id', 'tabComponent', tabMock); + expect(cut.tab).toEqual(tab); + }); + + test('that is library default tab instance is created when no alternative exists', () => { + accessorMock = jest.fn(() => { + const partial: Partial = { + options: { + components: { + contentComponent: contentMock, + }, + }, + }; + + return partial as DockviewComponent; + }); + + const cut = new DockviewPanelModel( + new accessorMock(), + 'id', + 'contentComponent' + ); + + expect(cut.tab instanceof DefaultTab).toBeTruthy(); + }); + + test('that the default content is created', () => { + accessorMock = jest.fn(() => { + const partial: Partial = { + options: { + components: { + contentComponent: jest.fn().mockImplementation(() => { + return contentMock; + }), + }, + }, + }; + + return partial as DockviewComponent; + }); + + const cut = new DockviewPanelModel( + new accessorMock(), + 'id', + 'contentComponent' + ); + + expect(cut.content).toEqual(contentMock); + }); + + test('that the framework content is created', () => { + const content = jest.fn(); + const contentFactory = jest.fn().mockImplementation(() => content); + + accessorMock = jest.fn(() => { + const partial: Partial = { + options: { + frameworkComponents: { + contentComponent: contentMock, + }, + frameworkComponentFactory: (< + Partial + >{ + content: { createComponent: contentFactory }, + }) as GroupPanelFrameworkComponentFactory, + }, + }; + + return partial as DockviewComponent; + }); + + const cut = new DockviewPanelModel( + new accessorMock(), + 'id', + 'contentComponent' + ); + + expect(contentFactory).toHaveBeenCalledWith( + 'id', + 'contentComponent', + contentMock + ); + expect(cut.content).toEqual(content); + }); }); diff --git a/packages/dockview-core/src/dockview/deserializer.ts b/packages/dockview-core/src/dockview/deserializer.ts index 5c6936c4d..82daa7381 100644 --- a/packages/dockview-core/src/dockview/deserializer.ts +++ b/packages/dockview-core/src/dockview/deserializer.ts @@ -1,9 +1,7 @@ -import { GroupviewPanelState, ITabRenderer } from './types'; +import { GroupviewPanelState } from './types'; import { DockviewGroupPanel } from './dockviewGroupPanel'; import { DockviewPanel, IDockviewPanel } from './dockviewPanel'; import { IDockviewComponent } from './dockviewComponent'; -import { createComponent } from '../panel/componentFactory'; -import { DefaultTab } from './components/tab/defaultTab'; import { DockviewPanelModel } from './dockviewPanelModel'; import { DockviewApi } from '../api/component.api'; @@ -14,7 +12,7 @@ export interface IPanelDeserializer { ): IDockviewPanel; } -// depreciated +// @depreciated interface LegacyState extends GroupviewPanelState { view?: { tab?: { id: string }; @@ -42,30 +40,6 @@ export class DefaultDockviewDeserialzier implements IPanelDeserializer { ? viewData.tab?.id : panelData.tabComponent; - let tab: ITabRenderer; - - if (tabComponent) { - tab = createComponent( - panelId, - tabComponent, - this.layout.options.tabComponents, - this.layout.options.frameworkTabComponents, - this.layout.options.frameworkComponentFactory?.tab, - () => new DefaultTab() - ); - } else if (this.layout.options.defaultTabComponent) { - tab = createComponent( - panelId, - this.layout.options.defaultTabComponent, - this.layout.options.tabComponents, - this.layout.options.frameworkTabComponents, - this.layout.options.frameworkComponentFactory?.tab, - () => new DefaultTab() - ); - } else { - tab = new DefaultTab(); - } - const view = new DockviewPanelModel( this.layout, panelId, diff --git a/packages/dockview-core/src/dockview/dockviewPanelModel.ts b/packages/dockview-core/src/dockview/dockviewPanelModel.ts index c8e2aee8f..b29bcd12c 100644 --- a/packages/dockview-core/src/dockview/dockviewPanelModel.ts +++ b/packages/dockview-core/src/dockview/dockviewPanelModel.ts @@ -43,8 +43,7 @@ export class DockviewPanelModel implements IDockviewPanelModel { readonly tabComponent?: string ) { this._content = this.createContentComponent(this.id, contentComponent); - this._tab = - this.createTabComponent(this.id, tabComponent) ?? new DefaultTab(); + this._tab = this.createTabComponent(this.id, tabComponent); } init(params: GroupPanelPartInitParameters): void { @@ -108,13 +107,26 @@ export class DockviewPanelModel implements IDockviewPanelModel { id: string, componentName?: string ): ITabRenderer { - return createComponent( - id, - componentName, - this.accessor.options.tabComponents || {}, - this.accessor.options.frameworkTabComponents, - this.accessor.options.frameworkComponentFactory?.tab, - () => new DefaultTab() - ); + if (componentName) { + return createComponent( + id, + componentName, + this.accessor.options.tabComponents, + this.accessor.options.frameworkTabComponents, + this.accessor.options.frameworkComponentFactory?.tab, + () => new DefaultTab() + ); + } else if (this.accessor.options.defaultTabComponent) { + return createComponent( + id, + this.accessor.options.defaultTabComponent, + this.accessor.options.tabComponents, + this.accessor.options.frameworkTabComponents, + this.accessor.options.frameworkComponentFactory?.tab, + () => new DefaultTab() + ); + } else { + return new DefaultTab(); + } } } From ec341f070616bd5c092dd5dbae8f8d6ec61f64d4 Mon Sep 17 00:00:00 2001 From: mathuo <6710312+mathuo@users.noreply.github.com> Date: Sun, 16 Apr 2023 22:06:48 +0100 Subject: [PATCH 14/22] test: adjust tsconfig for tests --- packages/dockview-core/jest.config.ts | 8 +++++++ .../gridview/baseComponentGridview.spec.ts | 2 +- .../groupview/dockviewGroupPanelModel.spec.ts | 22 ++++++++---------- packages/dockview/jest.config.ts | 2 +- .../src/__tests__/__test_utils__/utils.ts | 4 +--- .../src/__tests__/dockview/dockview.spec.tsx | 4 ++-- .../src/__tests__/gridview/gridview.spec.tsx | 4 ++-- .../src/__tests__/paneview/paneview.spec.tsx | 4 ++-- .../dockview/src/__tests__/react.spec.tsx | 4 ++-- .../react/dockview/dockview.spec.tsx | 4 ++-- .../react/gridview/gridview.spec.tsx | 4 ++-- .../react/paneview/paneview.spec.tsx | 4 ++-- .../src/__tests__/react/react.spec.tsx | 4 ++-- .../react/splitview/splitview.spec.tsx | 4 ++-- .../__tests__/splitview/splitview.spec.tsx | 4 ++-- tsconfig.base.json | 3 ++- tsconfig.test.json | 23 ++++--------------- 17 files changed, 47 insertions(+), 57 deletions(-) diff --git a/packages/dockview-core/jest.config.ts b/packages/dockview-core/jest.config.ts index e047e27b2..d5aa1ca7f 100644 --- a/packages/dockview-core/jest.config.ts +++ b/packages/dockview-core/jest.config.ts @@ -20,6 +20,14 @@ const config: JestConfigWithTsJest = { coverageDirectory: '/packages/dockview-core/coverage/', testResultsProcessor: 'jest-sonar-reporter', testEnvironment: 'jsdom', + transform: { + '^.+\\.tsx?$': [ + 'ts-jest', + { + tsconfig: '/tsconfig.test.json', + }, + ], + }, }; export default config; diff --git a/packages/dockview-core/src/__tests__/gridview/baseComponentGridview.spec.ts b/packages/dockview-core/src/__tests__/gridview/baseComponentGridview.spec.ts index 592598807..27e8925b1 100644 --- a/packages/dockview-core/src/__tests__/gridview/baseComponentGridview.spec.ts +++ b/packages/dockview-core/src/__tests__/gridview/baseComponentGridview.spec.ts @@ -114,7 +114,7 @@ describe('baseComponentGridview', () => { proportionalLayout: true, }); - const events: TestPanel[] = []; + const events: (TestPanel | undefined)[] = []; const disposable = new CompositeDisposable( cut.onDidAddGroup((event) => { diff --git a/packages/dockview-core/src/__tests__/groupview/dockviewGroupPanelModel.spec.ts b/packages/dockview-core/src/__tests__/groupview/dockviewGroupPanelModel.spec.ts index ef2ed7cad..b00522d05 100644 --- a/packages/dockview-core/src/__tests__/groupview/dockviewGroupPanelModel.spec.ts +++ b/packages/dockview-core/src/__tests__/groupview/dockviewGroupPanelModel.spec.ts @@ -18,11 +18,9 @@ import { LocalSelectionTransfer, PanelTransfer } from '../../dnd/dataTransfer'; import { CompositeDisposable } from '../../lifecycle'; import { DockviewPanelApi } from '../../api/dockviewPanelApi'; import { IDockviewPanel } from '../../dockview/dockviewPanel'; -import { - IDockviewPanelModel, - DockviewPanelModel, -} from '../../dockview/dockviewPanelModel'; +import { IDockviewPanelModel } from '../../dockview/dockviewPanelModel'; import { DockviewGroupPanel } from '../../dockview/dockviewGroupPanel'; +import { WatermarkRendererInitParameters } from '../../dockview/types'; enum GroupChangeKind2 { ADD_PANEL, @@ -76,7 +74,7 @@ class Watermark implements IWatermarkRenderer { return 'watermark-id'; } - init(params: GroupPanelPartInitParameters) { + init(params: WatermarkRendererInitParameters) { // } @@ -170,7 +168,7 @@ class TestHeaderPart implements ITabRenderer { export class TestPanel implements IDockviewPanel { private _group: DockviewGroupPanel | undefined; - private _params: IGroupPanelInitParameters; + private _params: IGroupPanelInitParameters | undefined; readonly view: IDockviewPanelModel; get title() { @@ -516,7 +514,7 @@ describe('groupview', () => { dockviewComponent, 'id', {}, - null + null as any ); expect(cut.toJSON()).toEqual({ @@ -539,7 +537,7 @@ describe('groupview', () => { dockviewComponent, 'id', {}, - null + null as any ); cut.locked = true; @@ -568,25 +566,25 @@ describe('groupview', () => { dockviewComponent, 'id', {}, - null + null as any ); const contentContainer = groupviewContainer .getElementsByClassName('content-container') .item(0)!.childNodes; - const panel1 = new TestPanel('id_1', null); + const panel1 = new TestPanel('id_1', null as any); cut.openPanel(panel1); expect(contentContainer.length).toBe(1); expect(contentContainer.item(0)).toBe(panel1.view.content.element); - const panel2 = new TestPanel('id_2', null); + const panel2 = new TestPanel('id_2', null as any); cut.openPanel(panel2); expect(contentContainer.length).toBe(1); expect(contentContainer.item(0)).toBe(panel2.view.content.element); - const panel3 = new TestPanel('id_2', null); + const panel3 = new TestPanel('id_2', null as any); cut.openPanel(panel3, { skipSetPanelActive: true }); expect(contentContainer.length).toBe(1); diff --git a/packages/dockview/jest.config.ts b/packages/dockview/jest.config.ts index f6b1969ca..b909c68ef 100644 --- a/packages/dockview/jest.config.ts +++ b/packages/dockview/jest.config.ts @@ -27,7 +27,7 @@ const config: JestConfigWithTsJest = { '^.+\\.tsx?$': [ 'ts-jest', { - tsconfig, + tsconfig: '/tsconfig.test.json', }, ], }, diff --git a/packages/dockview/src/__tests__/__test_utils__/utils.ts b/packages/dockview/src/__tests__/__test_utils__/utils.ts index ab8dc0a9e..675a30c17 100644 --- a/packages/dockview/src/__tests__/__test_utils__/utils.ts +++ b/packages/dockview/src/__tests__/__test_utils__/utils.ts @@ -1,5 +1,3 @@ -import * as React from 'react'; - export function setMockRefElement(node: Partial): void { const mockRef = { get current() { @@ -10,5 +8,5 @@ export function setMockRefElement(node: Partial): void { }, }; - jest.spyOn(React, 'useRef').mockReturnValueOnce(mockRef); + jest.spyOn(require('react'), 'useRef').mockReturnValueOnce(mockRef); } diff --git a/packages/dockview/src/__tests__/dockview/dockview.spec.tsx b/packages/dockview/src/__tests__/dockview/dockview.spec.tsx index 2c55b29b7..6fe0872a7 100644 --- a/packages/dockview/src/__tests__/dockview/dockview.spec.tsx +++ b/packages/dockview/src/__tests__/dockview/dockview.spec.tsx @@ -48,7 +48,7 @@ describe('gridview react', () => { render(); - expect(api.width).toBe(650); - expect(api.height).toBe(450); + expect(api!.width).toBe(650); + expect(api!.height).toBe(450); }); }); diff --git a/packages/dockview/src/__tests__/gridview/gridview.spec.tsx b/packages/dockview/src/__tests__/gridview/gridview.spec.tsx index 92f81f4c3..0881e00b4 100644 --- a/packages/dockview/src/__tests__/gridview/gridview.spec.tsx +++ b/packages/dockview/src/__tests__/gridview/gridview.spec.tsx @@ -59,7 +59,7 @@ describe('gridview react', () => { /> ); - expect(api.width).toBe(650); - expect(api.height).toBe(450); + expect(api!.width).toBe(650); + expect(api!.height).toBe(450); }); }); diff --git a/packages/dockview/src/__tests__/paneview/paneview.spec.tsx b/packages/dockview/src/__tests__/paneview/paneview.spec.tsx index 0a69965f0..830861e89 100644 --- a/packages/dockview/src/__tests__/paneview/paneview.spec.tsx +++ b/packages/dockview/src/__tests__/paneview/paneview.spec.tsx @@ -46,7 +46,7 @@ describe('gridview react', () => { render(); - expect(api.width).toBe(650); - expect(api.height).toBe(450); + expect(api!.width).toBe(650); + expect(api!.height).toBe(450); }); }); diff --git a/packages/dockview/src/__tests__/react.spec.tsx b/packages/dockview/src/__tests__/react.spec.tsx index 2abf76845..298d96fed 100644 --- a/packages/dockview/src/__tests__/react.spec.tsx +++ b/packages/dockview/src/__tests__/react.spec.tsx @@ -18,7 +18,7 @@ describe('react', () => { render(); - expect(api).toBeTruthy(); + expect(api!).toBeTruthy(); expect(screen.getByTestId('valueA').textContent).toBe('stringA'); expect(screen.getByTestId('valueB').textContent).toBe('42'); @@ -60,7 +60,7 @@ const TestWrapper = (props: { React.useEffect(() => { const cut = new ReactPart( - ref.current, + ref.current!, { addPortal: (portal: React.ReactPortal) => { setPortal((_) => [..._, portal]); diff --git a/packages/dockview/src/__tests__/react/dockview/dockview.spec.tsx b/packages/dockview/src/__tests__/react/dockview/dockview.spec.tsx index b8e9f1bcb..12048cac5 100644 --- a/packages/dockview/src/__tests__/react/dockview/dockview.spec.tsx +++ b/packages/dockview/src/__tests__/react/dockview/dockview.spec.tsx @@ -47,7 +47,7 @@ describe('dockview', () => { render(); - expect(api.width).toBe(650); - expect(api.height).toBe(450); + expect(api!.width).toBe(650); + expect(api!.height).toBe(450); }); }); diff --git a/packages/dockview/src/__tests__/react/gridview/gridview.spec.tsx b/packages/dockview/src/__tests__/react/gridview/gridview.spec.tsx index 8e76030d9..5e5ff70e9 100644 --- a/packages/dockview/src/__tests__/react/gridview/gridview.spec.tsx +++ b/packages/dockview/src/__tests__/react/gridview/gridview.spec.tsx @@ -58,7 +58,7 @@ describe('gridview react', () => { /> ); - expect(api.width).toBe(650); - expect(api.height).toBe(450); + expect(api!.width).toBe(650); + expect(api!.height).toBe(450); }); }); diff --git a/packages/dockview/src/__tests__/react/paneview/paneview.spec.tsx b/packages/dockview/src/__tests__/react/paneview/paneview.spec.tsx index 3223a8503..4dc8b8198 100644 --- a/packages/dockview/src/__tests__/react/paneview/paneview.spec.tsx +++ b/packages/dockview/src/__tests__/react/paneview/paneview.spec.tsx @@ -46,7 +46,7 @@ describe('gridview react', () => { render(); - expect(api.width).toBe(650); - expect(api.height).toBe(450); + expect(api!.width).toBe(650); + expect(api!.height).toBe(450); }); }); diff --git a/packages/dockview/src/__tests__/react/react.spec.tsx b/packages/dockview/src/__tests__/react/react.spec.tsx index f9799c0a5..b89d09d74 100644 --- a/packages/dockview/src/__tests__/react/react.spec.tsx +++ b/packages/dockview/src/__tests__/react/react.spec.tsx @@ -18,7 +18,7 @@ describe('react', () => { render(); - expect(api).toBeTruthy(); + expect(api!).toBeTruthy(); expect(screen.getByTestId('valueA').textContent).toBe('stringA'); expect(screen.getByTestId('valueB').textContent).toBe('42'); @@ -60,7 +60,7 @@ const TestWrapper = (props: { React.useEffect(() => { const cut = new ReactPart( - ref.current, + ref.current!, { addPortal: (portal: React.ReactPortal) => { setPortal((_) => [..._, portal]); diff --git a/packages/dockview/src/__tests__/react/splitview/splitview.spec.tsx b/packages/dockview/src/__tests__/react/splitview/splitview.spec.tsx index 852171aa9..9ffc01974 100644 --- a/packages/dockview/src/__tests__/react/splitview/splitview.spec.tsx +++ b/packages/dockview/src/__tests__/react/splitview/splitview.spec.tsx @@ -58,7 +58,7 @@ describe('splitview react', () => { /> ); - expect(api.width).toBe(650); - expect(api.height).toBe(450); + expect(api!.width).toBe(650); + expect(api!.height).toBe(450); }); }); diff --git a/packages/dockview/src/__tests__/splitview/splitview.spec.tsx b/packages/dockview/src/__tests__/splitview/splitview.spec.tsx index e0da9dd72..34e0027db 100644 --- a/packages/dockview/src/__tests__/splitview/splitview.spec.tsx +++ b/packages/dockview/src/__tests__/splitview/splitview.spec.tsx @@ -58,7 +58,7 @@ describe('splitview react', () => { /> ); - expect(api.width).toBe(650); - expect(api.height).toBe(450); + expect(api!.width).toBe(650); + expect(api!.height).toBe(450); }); }); diff --git a/tsconfig.base.json b/tsconfig.base.json index 77f0e8b99..2ca86a1c1 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -21,5 +21,6 @@ "ES2019", "DOM" ] - } + }, + "exclude": ["**/*.spec.ts"] } diff --git a/tsconfig.test.json b/tsconfig.test.json index d58b7d79f..5001874b2 100644 --- a/tsconfig.test.json +++ b/tsconfig.test.json @@ -1,22 +1,7 @@ - { + "extends": "./tsconfig.base.json", "compilerOptions": { - "module": "commonjs", - "target": "es2019", - "sourceMap": true, - "declaration": true, - "jsx": "react", - "noImplicitReturns": true, - "noImplicitAny": true, - "allowUnreachableCode": false, - "forceConsistentCasingInFileNames": true, - // "strict": true, - "strictBindCallApply": true, // pass - "alwaysStrict": true, // pass - "noImplicitThis": true, // pass - "strictFunctionTypes": true, // pass - "strictNullChecks": false, - "strictPropertyInitialization": false, + "jsx": "react-jsx" }, - "exclude": ["node_modules", "dist"] -} \ No newline at end of file + "include": ["**/*.spec.ts"] +} From bdecc00c8179985bf825f8daea454e1919c41243 Mon Sep 17 00:00:00 2001 From: mathuo <6710312+mathuo@users.noreply.github.com> Date: Sun, 16 Apr 2023 22:35:00 +0100 Subject: [PATCH 15/22] Update issue templates --- .github/ISSUE_TEMPLATE/bug_report.md | 33 +++++++++++++++++++++++ .github/ISSUE_TEMPLATE/feature_request.md | 20 ++++++++++++++ 2 files changed, 53 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 000000000..e8cd8f889 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,33 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: '' +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +[dockview.dev](https://dockview.dev) provides a number of examples with Code Sandbox templates. Are you able to produce the bug by forking one of those templates? Sharing a link to the forked sandbox with the bug would be extremely helpful. + +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Desktop (please complete the following information):** + - Browser [e.g. chrome, safari] + - Version [e.g. 22] + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 000000000..bbcbbe7d6 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: '' +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. From a147a282220546047b2afcf4f290e462c949f9d5 Mon Sep 17 00:00:00 2001 From: Matthew Pearson Date: Sat, 6 May 2023 02:19:38 -0700 Subject: [PATCH 16/22] bug: disable user-select on drag handles --- packages/dockview-core/src/splitview/splitview.scss | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/dockview-core/src/splitview/splitview.scss b/packages/dockview-core/src/splitview/splitview.scss index 99b91b925..c46ceb250 100644 --- a/packages/dockview-core/src/splitview/splitview.scss +++ b/packages/dockview-core/src/splitview/splitview.scss @@ -102,6 +102,10 @@ position: absolute; z-index: 99; outline: none; + user-select: none; + -webkit-user-select: none; // Safari + -moz-user-select: none; // Firefox + -ms-user-select: none; // IE 10 and IE 11 &:active { transition: background-color 0.1s ease-in-out; From 50789414ea13006851663dc1ad903959c2d12339 Mon Sep 17 00:00:00 2001 From: mathuo <6710312+mathuo@users.noreply.github.com> Date: Sun, 7 May 2023 15:37:00 +0100 Subject: [PATCH 17/22] chore: prepare v1.7.2 --- .../docs/blog/2023-05-07-dockview-1.7.2.md | 18 ++++++++++++++++++ .../basics.mdx | 0 .../components/_category_.json | 0 .../components/dockview.mdx | 0 .../components/gridview.mdx | 0 .../components/paneview.mdx | 0 .../components/splitview.mdx | 0 .../{version-1.7.1 => version-1.7.2}/index.mdx | 0 .../{version-1.7.1 => version-1.7.2}/theme.mdx | 0 ...debars.json => version-1.7.2-sidebars.json} | 0 packages/docs/versions.json | 2 +- 11 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 packages/docs/blog/2023-05-07-dockview-1.7.2.md rename packages/docs/versioned_docs/{version-1.7.1 => version-1.7.2}/basics.mdx (100%) rename packages/docs/versioned_docs/{version-1.7.1 => version-1.7.2}/components/_category_.json (100%) rename packages/docs/versioned_docs/{version-1.7.1 => version-1.7.2}/components/dockview.mdx (100%) rename packages/docs/versioned_docs/{version-1.7.1 => version-1.7.2}/components/gridview.mdx (100%) rename packages/docs/versioned_docs/{version-1.7.1 => version-1.7.2}/components/paneview.mdx (100%) rename packages/docs/versioned_docs/{version-1.7.1 => version-1.7.2}/components/splitview.mdx (100%) rename packages/docs/versioned_docs/{version-1.7.1 => version-1.7.2}/index.mdx (100%) rename packages/docs/versioned_docs/{version-1.7.1 => version-1.7.2}/theme.mdx (100%) rename packages/docs/versioned_sidebars/{version-1.7.1-sidebars.json => version-1.7.2-sidebars.json} (100%) diff --git a/packages/docs/blog/2023-05-07-dockview-1.7.2.md b/packages/docs/blog/2023-05-07-dockview-1.7.2.md new file mode 100644 index 000000000..e50745c10 --- /dev/null +++ b/packages/docs/blog/2023-05-07-dockview-1.7.2.md @@ -0,0 +1,18 @@ +--- +slug: dockview-1.7.2-release +title: Dockview 1.7.2 +tags: [release] +--- + +# Release Notes + +Please reference to docs @ [dockview.dev](https://dockview.dev). +If you feel anything is missing or unclear please let me know. + +## 🚀 Features + +## 🛠 Miscs + +- Fix bug with panel resize drag elements [#249](https://github.com/mathuo/dockview/issues/249) + +## 🔥 Breaking changes diff --git a/packages/docs/versioned_docs/version-1.7.1/basics.mdx b/packages/docs/versioned_docs/version-1.7.2/basics.mdx similarity index 100% rename from packages/docs/versioned_docs/version-1.7.1/basics.mdx rename to packages/docs/versioned_docs/version-1.7.2/basics.mdx diff --git a/packages/docs/versioned_docs/version-1.7.1/components/_category_.json b/packages/docs/versioned_docs/version-1.7.2/components/_category_.json similarity index 100% rename from packages/docs/versioned_docs/version-1.7.1/components/_category_.json rename to packages/docs/versioned_docs/version-1.7.2/components/_category_.json diff --git a/packages/docs/versioned_docs/version-1.7.1/components/dockview.mdx b/packages/docs/versioned_docs/version-1.7.2/components/dockview.mdx similarity index 100% rename from packages/docs/versioned_docs/version-1.7.1/components/dockview.mdx rename to packages/docs/versioned_docs/version-1.7.2/components/dockview.mdx diff --git a/packages/docs/versioned_docs/version-1.7.1/components/gridview.mdx b/packages/docs/versioned_docs/version-1.7.2/components/gridview.mdx similarity index 100% rename from packages/docs/versioned_docs/version-1.7.1/components/gridview.mdx rename to packages/docs/versioned_docs/version-1.7.2/components/gridview.mdx diff --git a/packages/docs/versioned_docs/version-1.7.1/components/paneview.mdx b/packages/docs/versioned_docs/version-1.7.2/components/paneview.mdx similarity index 100% rename from packages/docs/versioned_docs/version-1.7.1/components/paneview.mdx rename to packages/docs/versioned_docs/version-1.7.2/components/paneview.mdx diff --git a/packages/docs/versioned_docs/version-1.7.1/components/splitview.mdx b/packages/docs/versioned_docs/version-1.7.2/components/splitview.mdx similarity index 100% rename from packages/docs/versioned_docs/version-1.7.1/components/splitview.mdx rename to packages/docs/versioned_docs/version-1.7.2/components/splitview.mdx diff --git a/packages/docs/versioned_docs/version-1.7.1/index.mdx b/packages/docs/versioned_docs/version-1.7.2/index.mdx similarity index 100% rename from packages/docs/versioned_docs/version-1.7.1/index.mdx rename to packages/docs/versioned_docs/version-1.7.2/index.mdx diff --git a/packages/docs/versioned_docs/version-1.7.1/theme.mdx b/packages/docs/versioned_docs/version-1.7.2/theme.mdx similarity index 100% rename from packages/docs/versioned_docs/version-1.7.1/theme.mdx rename to packages/docs/versioned_docs/version-1.7.2/theme.mdx diff --git a/packages/docs/versioned_sidebars/version-1.7.1-sidebars.json b/packages/docs/versioned_sidebars/version-1.7.2-sidebars.json similarity index 100% rename from packages/docs/versioned_sidebars/version-1.7.1-sidebars.json rename to packages/docs/versioned_sidebars/version-1.7.2-sidebars.json diff --git a/packages/docs/versions.json b/packages/docs/versions.json index cc477f45b..519af9b47 100644 --- a/packages/docs/versions.json +++ b/packages/docs/versions.json @@ -1,3 +1,3 @@ [ - "1.7.1" + "1.7.2" ] From fda40a9fcb7a67f2219d6d301cd546e2d2212cbc Mon Sep 17 00:00:00 2001 From: mathuo <6710312+mathuo@users.noreply.github.com> Date: Sun, 7 May 2023 20:53:10 +0100 Subject: [PATCH 18/22] chore(release): publish v1.7.2 --- lerna.json | 2 +- packages/dockview-core/package.json | 2 +- packages/dockview/package.json | 4 ++-- packages/docs/package.json | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lerna.json b/lerna.json index 6a7a58efb..b2d59ced6 100644 --- a/lerna.json +++ b/lerna.json @@ -3,7 +3,7 @@ "packages/*" ], "useWorkspaces": true, - "version": "1.7.1", + "version": "1.7.2", "npmClient": "yarn", "command": { "publish": { diff --git a/packages/dockview-core/package.json b/packages/dockview-core/package.json index 7714ca455..cdceee251 100644 --- a/packages/dockview-core/package.json +++ b/packages/dockview-core/package.json @@ -1,6 +1,6 @@ { "name": "dockview-core", - "version": "1.7.1", + "version": "1.7.2", "description": "Zero dependency layout manager supporting tabs, grids and splitviews with ReactJS support", "main": "./dist/cjs/index.js", "types": "./dist/cjs/index.d.ts", diff --git a/packages/dockview/package.json b/packages/dockview/package.json index 01117bc7f..5f538135d 100644 --- a/packages/dockview/package.json +++ b/packages/dockview/package.json @@ -1,6 +1,6 @@ { "name": "dockview", - "version": "1.7.1", + "version": "1.7.2", "description": "Zero dependency layout manager supporting tabs, grids and splitviews with ReactJS support", "main": "./dist/cjs/index.js", "types": "./dist/cjs/index.d.ts", @@ -56,7 +56,7 @@ "author": "https://github.com/mathuo", "license": "MIT", "dependencies": { - "dockview-core": "^1.7.1" + "dockview-core": "^1.7.2" }, "devDependencies": { "@rollup/plugin-node-resolve": "^15.0.1", diff --git a/packages/docs/package.json b/packages/docs/package.json index 7e349fc32..c230bb45b 100644 --- a/packages/docs/package.json +++ b/packages/docs/package.json @@ -1,6 +1,6 @@ { "name": "dockview-docs", - "version": "1.7.1", + "version": "1.7.2", "private": true, "scripts": { "docusaurus": "docusaurus", @@ -23,7 +23,7 @@ "@minoru/react-dnd-treeview": "^3.4.3", "axios": "^1.3.3", "clsx": "^1.2.1", - "dockview": "^1.7.1", + "dockview": "^1.7.2", "prism-react-renderer": "^1.3.5", "react": "^18.2.0", "react-dnd": "^16.0.1", From 22018e0d289624b9710df105420f2c843265c197 Mon Sep 17 00:00:00 2001 From: mathuo <6710312+mathuo@users.noreply.github.com> Date: Mon, 8 May 2023 20:46:12 +0100 Subject: [PATCH 19/22] chore: docs --- packages/docs/docs/basics.mdx | 1 - packages/docs/docs/components/dockview.mdx | 6 ++- .../docs/sandboxes/dnd-dockview/src/app.tsx | 37 +++++++++++++------ .../externaldnd-dockview/src/treeview.tsx | 1 - 4 files changed, 30 insertions(+), 15 deletions(-) diff --git a/packages/docs/docs/basics.mdx b/packages/docs/docs/basics.mdx index 33d0270c0..b2c8ebcdb 100644 --- a/packages/docs/docs/basics.mdx +++ b/packages/docs/docs/basics.mdx @@ -8,7 +8,6 @@ import { SimpleSplitview2 } from '@site/src/components/simpleSplitview2'; # Basics -asd This section will take you through a number of concepts that can be applied to all dockview components. ## Panels diff --git a/packages/docs/docs/components/dockview.mdx b/packages/docs/docs/components/dockview.mdx index b6d1a3e46..c66b33e81 100644 --- a/packages/docs/docs/components/dockview.mdx +++ b/packages/docs/docs/components/dockview.mdx @@ -340,7 +340,9 @@ return ( ### Third Party Dnd Libraries -To be completed... +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. @@ -612,6 +614,8 @@ to the entire width of the group. For example: ### Tab Height +Tab height can be controlled through CSS. + diff --git a/packages/docs/sandboxes/dnd-dockview/src/app.tsx b/packages/docs/sandboxes/dnd-dockview/src/app.tsx index 7e3269c08..b5d4ad11a 100644 --- a/packages/docs/sandboxes/dnd-dockview/src/app.tsx +++ b/packages/docs/sandboxes/dnd-dockview/src/app.tsx @@ -18,6 +18,29 @@ const components = { }, }; +const DraggableElement = () => ( + { + if (event.dataTransfer) { + event.dataTransfer.effectAllowed = 'move'; + + event.dataTransfer.setData('text/plain', 'nothing'); + } + }} + style={{ + backgroundColor: 'orange', + padding: '0px 8px', + borderRadius: '4px', + width: '100px', + cursor: 'pointer', + }} + draggable={true} + > + Drag me + +); + const DndDockview = (props: { renderVisibleOnly: boolean }) => { const onReady = (event: DockviewReadyEvent) => { event.api.addPanel({ @@ -77,19 +100,9 @@ const DndDockview = (props: { renderVisibleOnly: boolean }) => { height: '100%', }} > -
- Drag me +
+
- { const [treeData, setTreeData] = useState(SampleData); const handleDrop = (newTreeData: any) => { - console.log('handleDrop'); setTreeData(newTreeData); }; return ( From 36299d08f9575250269bfa9ac3415b0c4a399f94 Mon Sep 17 00:00:00 2001 From: mathuo <6710312+mathuo@users.noreply.github.com> Date: Wed, 10 May 2023 22:06:12 +0100 Subject: [PATCH 20/22] feat: add typedocs --- .github/workflows/deploy-docs.yml | 6 ++++-- .gitignore | 1 + package.json | 7 +++++-- packages/dockview-core/typedoc.json | 7 ++----- packages/dockview/package.json | 3 +-- packages/dockview/tsconfig.json | 3 --- packages/dockview/typedoc.json | 8 +++----- packages/docs/package.json | 3 +-- packages/docs/scripts/package-docs.js | 8 -------- scripts/package-docs.js | 9 ++++++--- scripts/package.js | 15 -------------- tsconfig.json | 11 ++++++++++ typedoc.base.json | 4 ++++ typedoc.json | 7 +++++++ yarn.lock | 29 +++++++++++++++++---------- 15 files changed, 63 insertions(+), 58 deletions(-) delete mode 100644 packages/docs/scripts/package-docs.js delete mode 100644 scripts/package.js create mode 100644 tsconfig.json create mode 100644 typedoc.base.json create mode 100644 typedoc.json diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml index 9c242d324..6b82733a6 100644 --- a/.github/workflows/deploy-docs.yml +++ b/.github/workflows/deploy-docs.yml @@ -33,8 +33,10 @@ jobs: working-directory: packages/dockview - run: npm run build working-directory: packages/docs - - run: npm run deploy-docs - working-directory: packages/docs + - run: npm run docs + working-directory: . + - run: npm run package-docs + working-directory: . - name: Deploy 🚀 uses: JamesIves/github-pages-deploy-action@3.7.1 with: diff --git a/.gitignore b/.gitignore index 5f1ae726d..a38d78f1f 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,4 @@ test-report.xml *.code-workspace yarn-error.log /build +docs/ diff --git a/package.json b/package.json index 8d35430d0..cf33e48ae 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,9 @@ "bootstrap": "lerna bootstrap", "test:cov": "jest --coverage", "version-beta-build": "lerna version prerelease --preid beta", - "publish-app": "lerna publish" + "publish-app": "lerna publish", + "docs": "typedoc", + "package-docs": "node scripts/package-docs.js" }, "repository": { "type": "git", @@ -56,6 +58,7 @@ "ts-jest": "^29.0.5", "ts-loader": "^9.4.2", "tslib": "^2.5.0", + "typedoc": "^0.24.7", "typescript": "^4.9.5", "webpack": "^5.75.0", "webpack-cli": "^5.0.1", @@ -65,4 +68,4 @@ "jest": "^29.5.0", "ts-node": "^10.9.1" } -} \ No newline at end of file +} diff --git a/packages/dockview-core/typedoc.json b/packages/dockview-core/typedoc.json index 51c05e659..952d2032e 100644 --- a/packages/dockview-core/typedoc.json +++ b/packages/dockview-core/typedoc.json @@ -1,7 +1,4 @@ { - "out": "typedocs", - "entryPoints": ["./src/index.ts"], - "exclude": ["**/_test/**/*.*", "**/index.ts"], - "excludeExternals": true, - "excludePrivate": true + "extends": ["../../typedoc.base.json"], + "entryPoints": ["src/index.ts"] } diff --git a/packages/dockview/package.json b/packages/dockview/package.json index 5f538135d..19ec29c99 100644 --- a/packages/dockview/package.json +++ b/packages/dockview/package.json @@ -71,7 +71,6 @@ "react-dom": "^18.2.0", "rimraf": "^4.1.2", "rollup": "^3.15.0", - "rollup-plugin-postcss": "^4.0.2", - "typedoc": "^0.23.25" + "rollup-plugin-postcss": "^4.0.2" } } diff --git a/packages/dockview/tsconfig.json b/packages/dockview/tsconfig.json index f1a6f570c..023097d12 100644 --- a/packages/dockview/tsconfig.json +++ b/packages/dockview/tsconfig.json @@ -6,9 +6,6 @@ "jsx": "react", "rootDir": "src" }, - "paths": { - "dockview-core": "../dockview-core" - }, "include": ["src"], "exclude": ["**/node_modules", "src/__tests__"] } diff --git a/packages/dockview/typedoc.json b/packages/dockview/typedoc.json index 51c05e659..b97dde02c 100644 --- a/packages/dockview/typedoc.json +++ b/packages/dockview/typedoc.json @@ -1,7 +1,5 @@ { - "out": "typedocs", - "entryPoints": ["./src/index.ts"], - "exclude": ["**/_test/**/*.*", "**/index.ts"], - "excludeExternals": true, - "excludePrivate": true + "extends": ["../../typedoc.base.json"], + "entryPoints": ["src/index.ts"], + "exclude": ["**/dist/**"] } diff --git a/packages/docs/package.json b/packages/docs/package.json index c230bb45b..a5a509892 100644 --- a/packages/docs/package.json +++ b/packages/docs/package.json @@ -12,8 +12,7 @@ "serve": "docusaurus serve", "write-translations": "docusaurus write-translations", "write-heading-ids": "docusaurus write-heading-ids", - "typecheck": "tsc", - "deploy-docs": "node scripts/package-docs.js" + "typecheck": "tsc" }, "dependencies": { "@docusaurus/core": "^2.4.0", diff --git a/packages/docs/scripts/package-docs.js b/packages/docs/scripts/package-docs.js deleted file mode 100644 index 23ce40038..000000000 --- a/packages/docs/scripts/package-docs.js +++ /dev/null @@ -1,8 +0,0 @@ -const fs = require('fs-extra'); -const path = require('path'); - -const output = path.join(__dirname, '../../../build'); - -const docsDir = path.join(__dirname, '../build'); - -fs.copySync(docsDir, path.join(output)); diff --git a/scripts/package-docs.js b/scripts/package-docs.js index 11ac3cf51..92c469bac 100644 --- a/scripts/package-docs.js +++ b/scripts/package-docs.js @@ -1,8 +1,11 @@ const fs = require('fs-extra'); const path = require('path'); -const output = path.join(__dirname, '../'); +const output = path.join(__dirname, '../build'); -const docsDir = path.join(__dirname, '../docs/build'); +const websiteDir = path.join(__dirname, '../packages/docs/build'); -fs.copySync(docsDir, path.join(output, 'docs')); +const docsDir = path.join(__dirname, '../docs'); + +fs.copySync(websiteDir, output); +fs.copySync(docsDir, path.join(output, 'typedocs')); diff --git a/scripts/package.js b/scripts/package.js deleted file mode 100644 index 9b0238063..000000000 --- a/scripts/package.js +++ /dev/null @@ -1,15 +0,0 @@ -const fs = require('fs-extra'); -const path = require('path'); - -const output = path.join(__dirname, '../output'); - -const docsDir = path.join(__dirname, '../packages/dockview/typedocs'); -const webpackAppDir = path.join(__dirname, '../packages/dockview-demo/dist'); -// const storybookAppDir = path.join( -// __dirname, -// '../packages/dockview-demo/storybook-static' -// ); - -fs.copySync(docsDir, path.join(output, 'typedocs')); -fs.copySync(webpackAppDir, path.join(output, 'build')); -// fs.copySync(storybookAppDir, path.join(output, 'storybook-static')); diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 000000000..a9ac894b9 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,11 @@ +{ + "files": [], + "references": [ + { + "path": "./packages/dockview" + }, + { + "path": "./packages/dockview-core" + } + ] +} diff --git a/typedoc.base.json b/typedoc.base.json new file mode 100644 index 000000000..34bcb3e09 --- /dev/null +++ b/typedoc.base.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://typedoc.org/schema.json", + "includeVersion": true +} \ No newline at end of file diff --git a/typedoc.json b/typedoc.json new file mode 100644 index 000000000..5b8c53b5d --- /dev/null +++ b/typedoc.json @@ -0,0 +1,7 @@ +{ + "entryPoints": ["packages/dockview", "packages/dockview-core"], + "name": "Dockview", + "entryPointStrategy": "packages", + "includeVersion": true, + "logLevel": "Verbose" +} diff --git a/yarn.lock b/yarn.lock index f28480a93..cf1e11427 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9918,7 +9918,7 @@ markdown-escapes@^1.0.0: resolved "https://registry.yarnpkg.com/markdown-escapes/-/markdown-escapes-1.0.4.tgz#c95415ef451499d7602b91095f3c8e8975f78535" integrity sha512-8z4efJYk43E0upd0NbVXwgSTQs6cT3T06etieCMEg7dRbzCbxUCK/GHlX8mhHRDcp+OLlHkPKsvqQTCvsRl2cg== -marked@^4.2.12: +marked@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/marked/-/marked-4.3.0.tgz#796362821b019f734054582038b116481b456cf3" integrity sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A== @@ -10136,13 +10136,20 @@ minimatch@^6.1.6: dependencies: brace-expansion "^2.0.1" -minimatch@^7.1.3, minimatch@^7.4.1, minimatch@^7.4.2: +minimatch@^7.4.1, minimatch@^7.4.2: version "7.4.3" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-7.4.3.tgz#012cbf110a65134bb354ae9773b55256cdb045a2" integrity sha512-5UB4yYusDtkRPbRiy1cqZ1IpGNcJCGlEMG17RKzPddpyiPKoCdwohbED8g4QXT0ewCt8LTkQXuljsUfQ3FKM4A== dependencies: brace-expansion "^2.0.1" +minimatch@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.0.tgz#bfc8e88a1c40ffd40c172ddac3decb8451503b56" + integrity sha512-0jJj8AvgKqWN05mrwuqi8QYKx1WmYSUoKSxu5Qhs9prezTz10sxAHGNZe9J9cqIJzta8DWsleh2KaVaLl6Ru2w== + dependencies: + brace-expansion "^2.0.1" + minimist-options@4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/minimist-options/-/minimist-options-4.1.0.tgz#c0655713c53a8a2ebd77ffa247d342c40f010619" @@ -13180,9 +13187,9 @@ shelljs@^0.8.5: rechoir "^0.6.2" shiki@^0.14.1: - version "0.14.1" - resolved "https://registry.yarnpkg.com/shiki/-/shiki-0.14.1.tgz#9fbe082d0a8aa2ad63df4fbf2ee11ec924aa7ee1" - integrity sha512-+Jz4nBkCBe0mEDqo1eKRcCdjRtrCjozmcbTUjbPTX7OOJfEbTZzlUWlZtGe3Gb5oV1/jnojhG//YZc3rs9zSEw== + version "0.14.2" + resolved "https://registry.yarnpkg.com/shiki/-/shiki-0.14.2.tgz#d51440800b701392b31ce2336036058e338247a1" + integrity sha512-ltSZlSLOuSY0M0Y75KA+ieRaZ0Trf5Wl3gutE7jzLuIcWxLp5i/uEnLoQWNvgKXQ5OMpGkJnVMRLAuzjc0LJ2A== dependencies: ansi-sequence-parser "^1.1.0" jsonc-parser "^3.2.0" @@ -14249,14 +14256,14 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA== -typedoc@^0.23.25: - version "0.23.28" - resolved "https://registry.yarnpkg.com/typedoc/-/typedoc-0.23.28.tgz#3ce9c36ef1c273fa849d2dea18651855100d3ccd" - integrity sha512-9x1+hZWTHEQcGoP7qFmlo4unUoVJLB0H/8vfO/7wqTnZxg4kPuji9y3uRzEu0ZKez63OJAUmiGhUrtukC6Uj3w== +typedoc@^0.24.7: + version "0.24.7" + resolved "https://registry.yarnpkg.com/typedoc/-/typedoc-0.24.7.tgz#7eeb272a1894b3789acc1a94b3f2ae8e7330ee39" + integrity sha512-zzfKDFIZADA+XRIp2rMzLe9xZ6pt12yQOhCr7cD7/PBTjhPmMyMvGrkZ2lPNJitg3Hj1SeiYFNzCsSDrlpxpKw== dependencies: lunr "^2.3.9" - marked "^4.2.12" - minimatch "^7.1.3" + marked "^4.3.0" + minimatch "^9.0.0" shiki "^0.14.1" "typescript@^3 || ^4", typescript@^4.9.5: From 3c24579e905c39ddbc07d54f91790476591f19c4 Mon Sep 17 00:00:00 2001 From: mathuo <6710312+mathuo@users.noreply.github.com> Date: Mon, 22 May 2023 21:10:43 +0100 Subject: [PATCH 21/22] chore: docs: plain typescript examples --- .codesandbox/ci.json | 2 +- packages/docs/docs/components/dockview.mdx | 37 ++--- .../javascript/simple-dockview/package.json | 27 ++++ .../simple-dockview}/public/index.html | 0 .../javascript/simple-dockview/src/app.ts | 114 ++++++++++++++ .../simple-dockview/src/index.tsx} | 0 .../simple-dockview}/src/styles.css | 0 .../simple-dockview}/tsconfig.json | 0 .../vanilla-dockview/package.json | 0 .../vanilla-dockview/public/index.html | 44 ++++++ .../vanilla-dockview/src/app.ts | 0 .../javascript/vanilla-dockview/src/index.ts | 10 ++ .../vanilla-dockview/src/styles.css | 16 ++ .../javascript/vanilla-dockview/tsconfig.json | 20 +++ .../updatetitle-dockview/src/app.tsx | 9 +- .../docs/src/components/ui/container.scss | 0 packages/docs/src/components/ui/container.tsx | 147 ++++++++++++++++++ .../docs/src/components/ui/referenceTable.tsx | 42 +++++ packages/docs/src/components/ui/spinner.scss | 55 +++++++ packages/docs/src/components/ui/spinner.tsx | 13 ++ packages/docs/static/img/js-icon.svg | 4 + packages/docs/static/img/react-icon.svg | 9 ++ .../version-1.7.2/components/dockview.mdx | 4 +- 23 files changed, 526 insertions(+), 27 deletions(-) create mode 100644 packages/docs/sandboxes/javascript/simple-dockview/package.json rename packages/docs/sandboxes/{vanilla-dockview => javascript/simple-dockview}/public/index.html (100%) create mode 100644 packages/docs/sandboxes/javascript/simple-dockview/src/app.ts rename packages/docs/sandboxes/{vanilla-dockview/src/index.ts => javascript/simple-dockview/src/index.tsx} (100%) rename packages/docs/sandboxes/{vanilla-dockview => javascript/simple-dockview}/src/styles.css (100%) rename packages/docs/sandboxes/{vanilla-dockview => javascript/simple-dockview}/tsconfig.json (100%) rename packages/docs/sandboxes/{ => javascript}/vanilla-dockview/package.json (100%) create mode 100644 packages/docs/sandboxes/javascript/vanilla-dockview/public/index.html rename packages/docs/sandboxes/{ => javascript}/vanilla-dockview/src/app.ts (100%) create mode 100644 packages/docs/sandboxes/javascript/vanilla-dockview/src/index.ts create mode 100644 packages/docs/sandboxes/javascript/vanilla-dockview/src/styles.css create mode 100644 packages/docs/sandboxes/javascript/vanilla-dockview/tsconfig.json create mode 100644 packages/docs/src/components/ui/container.scss create mode 100644 packages/docs/src/components/ui/referenceTable.tsx create mode 100644 packages/docs/src/components/ui/spinner.scss create mode 100644 packages/docs/src/components/ui/spinner.tsx create mode 100644 packages/docs/static/img/js-icon.svg create mode 100644 packages/docs/static/img/react-icon.svg diff --git a/.codesandbox/ci.json b/.codesandbox/ci.json index 4d276169b..be48bfccc 100644 --- a/.codesandbox/ci.json +++ b/.codesandbox/ci.json @@ -22,7 +22,7 @@ "/packages/docs/sandboxes/simple-dockview", "/packages/docs/sandboxes/tabheight-dockview", "/packages/docs/sandboxes/updatetitle-dockview", - "/packages/docs/sandboxes/vanilla-dockview", + "/packages/docs/sandboxes/typescript/vanilla-dockview", "/packages/docs/sandboxes/watermark-dockview" ], "node": "16" diff --git a/packages/docs/docs/components/dockview.mdx b/packages/docs/docs/components/dockview.mdx index b6d1a3e46..8a4807c11 100644 --- a/packages/docs/docs/components/dockview.mdx +++ b/packages/docs/docs/components/dockview.mdx @@ -2,7 +2,10 @@ description: Dockview Documentation --- -import { Container } from '@site/src/components/ui/container'; +import { + Container, + MultiFrameworkContainer, +} from '@site/src/components/ui/container'; import Link from '@docusaurus/Link'; import useBaseUrl from '@docusaurus/useBaseUrl'; @@ -24,7 +27,9 @@ import RenderingDockview from '@site/sandboxes/rendering-dockview/src/app'; 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 { attach as attachDockviewVanilla } from '@site/sandboxes/vanilla-dockview/src/app'; + +import { attach as attachDockviewVanilla } from '@site/sandboxes/javascript/vanilla-dockview/src/app'; +import { attach as attachSimpleDockview } from '@site/sandboxes/javascript/simple-dockview/src/app'; # Dockview @@ -32,12 +37,16 @@ import { attach as attachDockviewVanilla } from '@site/sandboxes/vanilla-dockvie Dockview is an abstraction built on top of [Gridviews](./gridview) where each view is a container of many tabbed panels. - - - + -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. +
+ +> 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. ## DockviewReact Component @@ -705,19 +714,11 @@ If you wish to interact with the drop event from one dockview instance in anothe -### Example - -hello +### Window-like mananger with tabs -hello 2 - -
- -
- -## VanillaJS +## Vanilla JS > Note: This section is experimental and support for Vanilla JS is a work in progress. @@ -728,6 +729,6 @@ The core library is published as an independant package under the name `dockview > `dockview-core` is a dependency of `dockview` and automatically installed during the installation process of `dockview` via `npm install dockview`. diff --git a/packages/docs/sandboxes/javascript/simple-dockview/package.json b/packages/docs/sandboxes/javascript/simple-dockview/package.json new file mode 100644 index 000000000..bc36e6b5d --- /dev/null +++ b/packages/docs/sandboxes/javascript/simple-dockview/package.json @@ -0,0 +1,27 @@ +{ + "name": "javascript-simple-dockview", + "description": "", + "keywords": [ + "dockview" + ], + "version": "1.0.0", + "main": "src/index.ts", + "dependencies": { + "dockview-core": "*" + }, + "devDependencies": { + "typescript": "^4.9.5" + }, + "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" + ] +} \ No newline at end of file diff --git a/packages/docs/sandboxes/vanilla-dockview/public/index.html b/packages/docs/sandboxes/javascript/simple-dockview/public/index.html similarity index 100% rename from packages/docs/sandboxes/vanilla-dockview/public/index.html rename to packages/docs/sandboxes/javascript/simple-dockview/public/index.html diff --git a/packages/docs/sandboxes/javascript/simple-dockview/src/app.ts b/packages/docs/sandboxes/javascript/simple-dockview/src/app.ts new file mode 100644 index 000000000..e368f3e42 --- /dev/null +++ b/packages/docs/sandboxes/javascript/simple-dockview/src/app.ts @@ -0,0 +1,114 @@ +import { + DockviewComponent, + IContentRenderer, + IGroupPanelInitParameters, +} from 'dockview-core'; + +class DefaultPanel implements IContentRenderer { + private _element: HTMLElement; + + get element(): HTMLElement { + return this._element; + } + + constructor() { + this._element = document.createElement('div'); + this._element.style.padding = '20px'; + this._element.style.color = 'white'; + } + + init(params: IGroupPanelInitParameters): void { + this._element.textContent = params.params.title; + } +} + +export function attach(parent: HTMLElement): { + dispose: () => void; +} { + const element = document.createElement('div'); + element.className = 'dockview-theme-abyss'; + element.style.height = '100%'; + element.style.width = '100%'; + + const dockview = new DockviewComponent({ + components: { + default: DefaultPanel, + }, + parentElement: element, + }); + + parent.appendChild(element); + + const { clientWidth, clientHeight } = parent; + dockview.layout(clientWidth, clientHeight); + + const panel = dockview.addPanel({ + id: 'panel_1', + component: 'default', + params: { + title: 'Panel 1', + }, + }); + + panel.group.locked = true; + panel.group.header.hidden = true; + + dockview.addPanel({ + id: 'panel_2', + component: 'default', + params: { + title: 'Panel 2', + }, + }); + + dockview.addPanel({ + id: 'panel_3', + component: 'default', + params: { + title: 'Panel 3', + }, + }); + + dockview.addPanel({ + id: 'panel_4', + component: 'default', + params: { + title: 'Panel 4', + }, + position: { referencePanel: 'panel_1', direction: 'right' }, + }); + + const panel5 = dockview.addPanel({ + id: 'panel_5', + component: 'default', + params: { + title: 'Panel 5', + }, + position: { referencePanel: 'panel_3', direction: 'right' }, + }); + + dockview.addPanel({ + id: 'panel_6', + component: 'default', + params: { + title: 'Panel 6', + }, + position: { referencePanel: 'panel_5', direction: 'below' }, + }); + + dockview.addPanel({ + id: 'panel_7', + component: 'default', + params: { + title: 'Panel 7', + }, + position: { referencePanel: 'panel_6', direction: 'right' }, + }); + + return { + dispose: () => { + dockview.dispose(); + element.remove(); + }, + }; +} diff --git a/packages/docs/sandboxes/vanilla-dockview/src/index.ts b/packages/docs/sandboxes/javascript/simple-dockview/src/index.tsx similarity index 100% rename from packages/docs/sandboxes/vanilla-dockview/src/index.ts rename to packages/docs/sandboxes/javascript/simple-dockview/src/index.tsx diff --git a/packages/docs/sandboxes/vanilla-dockview/src/styles.css b/packages/docs/sandboxes/javascript/simple-dockview/src/styles.css similarity index 100% rename from packages/docs/sandboxes/vanilla-dockview/src/styles.css rename to packages/docs/sandboxes/javascript/simple-dockview/src/styles.css diff --git a/packages/docs/sandboxes/vanilla-dockview/tsconfig.json b/packages/docs/sandboxes/javascript/simple-dockview/tsconfig.json similarity index 100% rename from packages/docs/sandboxes/vanilla-dockview/tsconfig.json rename to packages/docs/sandboxes/javascript/simple-dockview/tsconfig.json diff --git a/packages/docs/sandboxes/vanilla-dockview/package.json b/packages/docs/sandboxes/javascript/vanilla-dockview/package.json similarity index 100% rename from packages/docs/sandboxes/vanilla-dockview/package.json rename to packages/docs/sandboxes/javascript/vanilla-dockview/package.json diff --git a/packages/docs/sandboxes/javascript/vanilla-dockview/public/index.html b/packages/docs/sandboxes/javascript/vanilla-dockview/public/index.html new file mode 100644 index 000000000..1f8a52426 --- /dev/null +++ b/packages/docs/sandboxes/javascript/vanilla-dockview/public/index.html @@ -0,0 +1,44 @@ + + + + + + + + + + + + + React App + + + + +
+ + + + diff --git a/packages/docs/sandboxes/vanilla-dockview/src/app.ts b/packages/docs/sandboxes/javascript/vanilla-dockview/src/app.ts similarity index 100% rename from packages/docs/sandboxes/vanilla-dockview/src/app.ts rename to packages/docs/sandboxes/javascript/vanilla-dockview/src/app.ts diff --git a/packages/docs/sandboxes/javascript/vanilla-dockview/src/index.ts b/packages/docs/sandboxes/javascript/vanilla-dockview/src/index.ts new file mode 100644 index 000000000..249b56017 --- /dev/null +++ b/packages/docs/sandboxes/javascript/vanilla-dockview/src/index.ts @@ -0,0 +1,10 @@ +import './styles.css'; +import 'dockview-core/dist/styles/dockview.css'; + +import { attach } from './app'; + +const rootElement = document.getElementById('root'); + +if (rootElement) { + attach(rootElement); +} diff --git a/packages/docs/sandboxes/javascript/vanilla-dockview/src/styles.css b/packages/docs/sandboxes/javascript/vanilla-dockview/src/styles.css new file mode 100644 index 000000000..92b6a1b36 --- /dev/null +++ b/packages/docs/sandboxes/javascript/vanilla-dockview/src/styles.css @@ -0,0 +1,16 @@ +body { + margin: 0px; + color: white; + font-family: sans-serif; + text-align: center; +} + +#root { + height: 100vh; + width: 100vw; +} + +.app { + height: 100%; + +} diff --git a/packages/docs/sandboxes/javascript/vanilla-dockview/tsconfig.json b/packages/docs/sandboxes/javascript/vanilla-dockview/tsconfig.json new file mode 100644 index 000000000..6e13e47b5 --- /dev/null +++ b/packages/docs/sandboxes/javascript/vanilla-dockview/tsconfig.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "outDir": "build/dist", + "module": "esnext", + "target": "es5", + "lib": ["es6", "dom"], + "sourceMap": true, + "allowJs": true, + "jsx": "react-jsx", + "moduleResolution": "node", + "rootDir": "src", + "forceConsistentCasingInFileNames": true, + "noImplicitReturns": true, + "noImplicitThis": true, + "noImplicitAny": true, + "strictNullChecks": true, + "suppressImplicitAnyIndexErrors": true, + "noUnusedLocals": true + } +} diff --git a/packages/docs/sandboxes/updatetitle-dockview/src/app.tsx b/packages/docs/sandboxes/updatetitle-dockview/src/app.tsx index 93a3b1762..8c9115df6 100644 --- a/packages/docs/sandboxes/updatetitle-dockview/src/app.tsx +++ b/packages/docs/sandboxes/updatetitle-dockview/src/app.tsx @@ -6,10 +6,8 @@ import { import * as React from 'react'; const components = { - default: ( - props: IDockviewPanelProps<{ title: string; myValue: string }> - ) => { - const [title, setTitle] = React.useState(props.params.title); + default: (props: IDockviewPanelProps<{ myValue: string }>) => { + const [title, setTitle] = React.useState(props.api.title); const onChange = (event: React.ChangeEvent) => { setTitle(event.target.value); @@ -27,6 +25,7 @@ const components = {
+ {JSON.stringify(Object.keys(props.params))} ); }, @@ -44,7 +43,6 @@ export const App: React.FC = () => { id: 'panel_2', component: 'default', title: 'Panel 2', - position: { referencePanel: panel }, }); @@ -60,7 +58,6 @@ export const App: React.FC = () => { id: 'panel_4', component: 'default', title: 'Panel 4', - position: { referencePanel: panel3 }, }); }; diff --git a/packages/docs/src/components/ui/container.scss b/packages/docs/src/components/ui/container.scss new file mode 100644 index 000000000..e69de29bb diff --git a/packages/docs/src/components/ui/container.tsx b/packages/docs/src/components/ui/container.tsx index 1345315bf..5ad78067e 100644 --- a/packages/docs/src/components/ui/container.tsx +++ b/packages/docs/src/components/ui/container.tsx @@ -1,5 +1,8 @@ import * as React from 'react'; import { CodeSandboxButton } from './codeSandboxButton'; +import useBaseUrl from '@docusaurus/useBaseUrl'; +import './container.scss'; +import { Spinner } from './spinner'; export const Container = (props: { children?: React.ReactNode; @@ -41,3 +44,147 @@ export const Container = (props: { ); }; + +const ReactIcon = (props: { height: number; width: number }) => { + return ( + + ); +}; + +const JavascriptIcon = (props: { height: number; width: number }) => { + return ( + + ); +}; + +export const MultiFrameworkContainer = (props: { + react: React.FC; + typescript: (parent: HTMLElement) => { dispose: () => void }; + sandboxId: string; + height?: number; +}) => { + const ref = React.useRef(null); + + const [framework, setFramework] = React.useState('React'); + + const [animation, setAnimation] = React.useState(false); + + React.useEffect(() => { + setAnimation(true); + + setTimeout(() => { + setAnimation(false); + }, 500); + }, [framework]); + + React.useEffect(() => { + if (!ref.current) { + return; + } + + if (framework === 'Javascript') { + const disposable = props.typescript(ref.current); + + return () => { + disposable.dispose(); + }; + } + + return; + }, [props.typescript, framework]); + + const sandboxId = React.useMemo(() => { + if (framework === 'Javascript') { + return `javascript/${props.sandboxId}`; + } + return props.sandboxId; + }, [props.sandboxId, framework]); + + return ( + <> +
+ {animation && ( +
+ +
+ )} + {framework === 'React' && } +
+
+
+ {framework === 'React' ? ( + + ) : ( + + )} + +
+ + +
+ + ); +}; diff --git a/packages/docs/src/components/ui/referenceTable.tsx b/packages/docs/src/components/ui/referenceTable.tsx new file mode 100644 index 000000000..1423122cc --- /dev/null +++ b/packages/docs/src/components/ui/referenceTable.tsx @@ -0,0 +1,42 @@ +import * as React from 'react'; + +export interface ReferenceProps { + props: { + prop: string; + default?: string; + type: string; + }[]; +} + +export const ReferenceTable = (props: ReferenceProps) => { + return ( + + + + + + + + + + {props.props.map((property) => { + return ( + + + + + + ); + })} + +
PropertyTypeDefault
+ {property.prop} + + {property.type} + + {property.default !== undefined && ( + {property.default} + )} +
+ ); +}; diff --git a/packages/docs/src/components/ui/spinner.scss b/packages/docs/src/components/ui/spinner.scss new file mode 100644 index 000000000..847ee4d22 --- /dev/null +++ b/packages/docs/src/components/ui/spinner.scss @@ -0,0 +1,55 @@ +.lds-ellipsis { + display: inline-block; + position: relative; + width: 80px; + height: 80px; +} +.lds-ellipsis div { + position: absolute; + top: 33px; + width: 13px; + height: 13px; + border-radius: 50%; + background: #fff; + animation-timing-function: cubic-bezier(0, 1, 1, 0); +} +.lds-ellipsis div:nth-child(1) { + left: 8px; + animation: lds-ellipsis1 0.6s infinite; +} +.lds-ellipsis div:nth-child(2) { + left: 8px; + animation: lds-ellipsis2 0.6s infinite; +} +.lds-ellipsis div:nth-child(3) { + left: 32px; + animation: lds-ellipsis2 0.6s infinite; +} +.lds-ellipsis div:nth-child(4) { + left: 56px; + animation: lds-ellipsis3 0.6s infinite; +} +@keyframes lds-ellipsis1 { + 0% { + transform: scale(0); + } + 100% { + transform: scale(1); + } +} +@keyframes lds-ellipsis3 { + 0% { + transform: scale(1); + } + 100% { + transform: scale(0); + } +} +@keyframes lds-ellipsis2 { + 0% { + transform: translate(0, 0); + } + 100% { + transform: translate(24px, 0); + } +} diff --git a/packages/docs/src/components/ui/spinner.tsx b/packages/docs/src/components/ui/spinner.tsx new file mode 100644 index 000000000..9627e3762 --- /dev/null +++ b/packages/docs/src/components/ui/spinner.tsx @@ -0,0 +1,13 @@ +import * as React from 'react'; +import './spinner.scss'; + +export const Spinner = () => { + return ( +
+
+
+
+
+
+ ); +}; diff --git a/packages/docs/static/img/js-icon.svg b/packages/docs/static/img/js-icon.svg new file mode 100644 index 000000000..9650ca78e --- /dev/null +++ b/packages/docs/static/img/js-icon.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/packages/docs/static/img/react-icon.svg b/packages/docs/static/img/react-icon.svg new file mode 100644 index 000000000..ea77a618d --- /dev/null +++ b/packages/docs/static/img/react-icon.svg @@ -0,0 +1,9 @@ + + React Logo + + + + + + + diff --git a/packages/docs/versioned_docs/version-1.7.2/components/dockview.mdx b/packages/docs/versioned_docs/version-1.7.2/components/dockview.mdx index b6d1a3e46..8097910eb 100644 --- a/packages/docs/versioned_docs/version-1.7.2/components/dockview.mdx +++ b/packages/docs/versioned_docs/version-1.7.2/components/dockview.mdx @@ -24,7 +24,7 @@ import RenderingDockview from '@site/sandboxes/rendering-dockview/src/app'; 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 { attach as attachDockviewVanilla } from '@site/sandboxes/vanilla-dockview/src/app'; +import { attach as attachDockviewVanilla } from '@site/sandboxes/javascript/vanilla-dockview/src/app'; # Dockview @@ -728,6 +728,6 @@ The core library is published as an independant package under the name `dockview > `dockview-core` is a dependency of `dockview` and automatically installed during the installation process of `dockview` via `npm install dockview`. From 32746e248d81efb40106e9e9de9a35b8465831f7 Mon Sep 17 00:00:00 2001 From: mathuo <6710312+mathuo@users.noreply.github.com> Date: Mon, 22 May 2023 21:37:50 +0100 Subject: [PATCH 22/22] chore: docs (plain ts examples) --- .codesandbox/ci.json | 7 +- .gitignore | 2 +- packages/docs/docs/components/dockview.mdx | 18 +- .../constraints-dockview/tsconfig.json | 35 ++-- .../customheader-dockview/tsconfig.json | 35 ++-- .../sandboxes/demo-dockview/tsconfig.json | 35 ++-- .../docs/sandboxes/dnd-dockview/tsconfig.json | 35 ++-- .../docs/sandboxes/dockview-app/tsconfig.json | 35 ++-- .../sandboxes/events-dockview/tsconfig.json | 35 ++-- .../externaldnd-dockview/tsconfig.json | 35 ++-- .../fullwidthtab-dockview/tsconfig.json | 35 ++-- .../groupcontrol-dockview/tsconfig.json | 35 ++-- .../fullwidthtab-dockview/package.json | 27 +++ .../fullwidthtab-dockview/public/index.html | 44 +++++ .../fullwidthtab-dockview/src/app.scss | 17 ++ .../fullwidthtab-dockview/src/app.ts | 171 ++++++++++++++++++ .../src/index.ts} | 0 .../fullwidthtab-dockview/src/styles.css | 16 ++ .../fullwidthtab-dockview/tsconfig.json | 19 ++ .../javascript/simple-dockview/src/app.ts | 6 + .../javascript/simple-dockview/src/index.ts | 10 + .../javascript/simple-dockview/tsconfig.json | 35 ++-- .../tabheight-dockview/package.json | 27 +++ .../tabheight-dockview/public/index.html | 44 +++++ .../tabheight-dockview/src/app.scss | 3 + .../javascript/tabheight-dockview/src/app.ts | 110 +++++++++++ .../tabheight-dockview/src/index.ts | 10 + .../tabheight-dockview/src/styles.css | 16 ++ .../tabheight-dockview/tsconfig.json | 19 ++ .../javascript/vanilla-dockview/tsconfig.json | 1 - .../sandboxes/layout-dockview/tsconfig.json | 35 ++-- .../nativeapp-dockview/tsconfig.json | 35 ++-- .../sandboxes/nested-dockview/tsconfig.json | 35 ++-- .../rendering-dockview/tsconfig.json | 35 ++-- .../sandboxes/resize-dockview/tsconfig.json | 35 ++-- .../resizecontainer-dockview/tsconfig.json | 35 ++-- .../sandboxes/simple-dockview/tsconfig.json | 35 ++-- .../tabheight-dockview/tsconfig.json | 35 ++-- .../updatetitle-dockview/tsconfig.json | 35 ++-- .../watermark-dockview/tsconfig.json | 35 ++-- 40 files changed, 897 insertions(+), 370 deletions(-) create mode 100644 packages/docs/sandboxes/javascript/fullwidthtab-dockview/package.json create mode 100644 packages/docs/sandboxes/javascript/fullwidthtab-dockview/public/index.html create mode 100644 packages/docs/sandboxes/javascript/fullwidthtab-dockview/src/app.scss create mode 100644 packages/docs/sandboxes/javascript/fullwidthtab-dockview/src/app.ts rename packages/docs/sandboxes/javascript/{simple-dockview/src/index.tsx => fullwidthtab-dockview/src/index.ts} (100%) create mode 100644 packages/docs/sandboxes/javascript/fullwidthtab-dockview/src/styles.css create mode 100644 packages/docs/sandboxes/javascript/fullwidthtab-dockview/tsconfig.json create mode 100644 packages/docs/sandboxes/javascript/simple-dockview/src/index.ts create mode 100644 packages/docs/sandboxes/javascript/tabheight-dockview/package.json create mode 100644 packages/docs/sandboxes/javascript/tabheight-dockview/public/index.html create mode 100644 packages/docs/sandboxes/javascript/tabheight-dockview/src/app.scss create mode 100644 packages/docs/sandboxes/javascript/tabheight-dockview/src/app.ts create mode 100644 packages/docs/sandboxes/javascript/tabheight-dockview/src/index.ts create mode 100644 packages/docs/sandboxes/javascript/tabheight-dockview/src/styles.css create mode 100644 packages/docs/sandboxes/javascript/tabheight-dockview/tsconfig.json diff --git a/.codesandbox/ci.json b/.codesandbox/ci.json index be48bfccc..a6a13434a 100644 --- a/.codesandbox/ci.json +++ b/.codesandbox/ci.json @@ -22,8 +22,11 @@ "/packages/docs/sandboxes/simple-dockview", "/packages/docs/sandboxes/tabheight-dockview", "/packages/docs/sandboxes/updatetitle-dockview", - "/packages/docs/sandboxes/typescript/vanilla-dockview", - "/packages/docs/sandboxes/watermark-dockview" + "/packages/docs/sandboxes/watermark-dockview", + "/packages/docs/sandboxes/typescript/fullwidthtab-dockview", + "/packages/docs/sandboxes/typescript/simple-dockview", + "/packages/docs/sandboxes/typescript/tabheight-dockview", + "/packages/docs/sandboxes/typescript/vanilla-dockview" ], "node": "16" } \ No newline at end of file diff --git a/.gitignore b/.gitignore index a38d78f1f..e3f290550 100644 --- a/.gitignore +++ b/.gitignore @@ -13,4 +13,4 @@ test-report.xml *.code-workspace yarn-error.log /build -docs/ +/docs/ diff --git a/packages/docs/docs/components/dockview.mdx b/packages/docs/docs/components/dockview.mdx index d32c08a9b..835dee189 100644 --- a/packages/docs/docs/components/dockview.mdx +++ b/packages/docs/docs/components/dockview.mdx @@ -30,6 +30,8 @@ import DockviewTabheight from '@site/sandboxes/tabheight-dockview/src/app'; import { attach as attachDockviewVanilla } from '@site/sandboxes/javascript/vanilla-dockview/src/app'; import { attach as attachSimpleDockview } from '@site/sandboxes/javascript/simple-dockview/src/app'; +import { attach as attachTabHeightDockview } from '@site/sandboxes/javascript/tabheight-dockview/src/app'; +import { attach as attachNativeDockview } from '@site/sandboxes/javascript/fullwidthtab-dockview/src/app'; # Dockview @@ -617,17 +619,21 @@ to the entire width of the group. For example: ``` - - - + ### Tab Height Tab height can be controlled through CSS. - - - + ## Groups diff --git a/packages/docs/sandboxes/constraints-dockview/tsconfig.json b/packages/docs/sandboxes/constraints-dockview/tsconfig.json index 6e13e47b5..8aebe3efe 100644 --- a/packages/docs/sandboxes/constraints-dockview/tsconfig.json +++ b/packages/docs/sandboxes/constraints-dockview/tsconfig.json @@ -1,20 +1,19 @@ { - "compilerOptions": { - "outDir": "build/dist", - "module": "esnext", - "target": "es5", - "lib": ["es6", "dom"], - "sourceMap": true, - "allowJs": true, - "jsx": "react-jsx", - "moduleResolution": "node", - "rootDir": "src", - "forceConsistentCasingInFileNames": true, - "noImplicitReturns": true, - "noImplicitThis": true, - "noImplicitAny": true, - "strictNullChecks": true, - "suppressImplicitAnyIndexErrors": true, - "noUnusedLocals": true - } + "compilerOptions": { + "outDir": "build/dist", + "module": "esnext", + "target": "es5", + "lib": ["es6", "dom"], + "sourceMap": true, + "allowJs": true, + "jsx": "react-jsx", + "moduleResolution": "node", + "rootDir": "src", + "forceConsistentCasingInFileNames": true, + "noImplicitReturns": true, + "noImplicitThis": true, + "noImplicitAny": true, + "strictNullChecks": true, + "noUnusedLocals": true + } } diff --git a/packages/docs/sandboxes/customheader-dockview/tsconfig.json b/packages/docs/sandboxes/customheader-dockview/tsconfig.json index 6e13e47b5..8aebe3efe 100644 --- a/packages/docs/sandboxes/customheader-dockview/tsconfig.json +++ b/packages/docs/sandboxes/customheader-dockview/tsconfig.json @@ -1,20 +1,19 @@ { - "compilerOptions": { - "outDir": "build/dist", - "module": "esnext", - "target": "es5", - "lib": ["es6", "dom"], - "sourceMap": true, - "allowJs": true, - "jsx": "react-jsx", - "moduleResolution": "node", - "rootDir": "src", - "forceConsistentCasingInFileNames": true, - "noImplicitReturns": true, - "noImplicitThis": true, - "noImplicitAny": true, - "strictNullChecks": true, - "suppressImplicitAnyIndexErrors": true, - "noUnusedLocals": true - } + "compilerOptions": { + "outDir": "build/dist", + "module": "esnext", + "target": "es5", + "lib": ["es6", "dom"], + "sourceMap": true, + "allowJs": true, + "jsx": "react-jsx", + "moduleResolution": "node", + "rootDir": "src", + "forceConsistentCasingInFileNames": true, + "noImplicitReturns": true, + "noImplicitThis": true, + "noImplicitAny": true, + "strictNullChecks": true, + "noUnusedLocals": true + } } diff --git a/packages/docs/sandboxes/demo-dockview/tsconfig.json b/packages/docs/sandboxes/demo-dockview/tsconfig.json index 6e13e47b5..8aebe3efe 100644 --- a/packages/docs/sandboxes/demo-dockview/tsconfig.json +++ b/packages/docs/sandboxes/demo-dockview/tsconfig.json @@ -1,20 +1,19 @@ { - "compilerOptions": { - "outDir": "build/dist", - "module": "esnext", - "target": "es5", - "lib": ["es6", "dom"], - "sourceMap": true, - "allowJs": true, - "jsx": "react-jsx", - "moduleResolution": "node", - "rootDir": "src", - "forceConsistentCasingInFileNames": true, - "noImplicitReturns": true, - "noImplicitThis": true, - "noImplicitAny": true, - "strictNullChecks": true, - "suppressImplicitAnyIndexErrors": true, - "noUnusedLocals": true - } + "compilerOptions": { + "outDir": "build/dist", + "module": "esnext", + "target": "es5", + "lib": ["es6", "dom"], + "sourceMap": true, + "allowJs": true, + "jsx": "react-jsx", + "moduleResolution": "node", + "rootDir": "src", + "forceConsistentCasingInFileNames": true, + "noImplicitReturns": true, + "noImplicitThis": true, + "noImplicitAny": true, + "strictNullChecks": true, + "noUnusedLocals": true + } } diff --git a/packages/docs/sandboxes/dnd-dockview/tsconfig.json b/packages/docs/sandboxes/dnd-dockview/tsconfig.json index 6e13e47b5..8aebe3efe 100644 --- a/packages/docs/sandboxes/dnd-dockview/tsconfig.json +++ b/packages/docs/sandboxes/dnd-dockview/tsconfig.json @@ -1,20 +1,19 @@ { - "compilerOptions": { - "outDir": "build/dist", - "module": "esnext", - "target": "es5", - "lib": ["es6", "dom"], - "sourceMap": true, - "allowJs": true, - "jsx": "react-jsx", - "moduleResolution": "node", - "rootDir": "src", - "forceConsistentCasingInFileNames": true, - "noImplicitReturns": true, - "noImplicitThis": true, - "noImplicitAny": true, - "strictNullChecks": true, - "suppressImplicitAnyIndexErrors": true, - "noUnusedLocals": true - } + "compilerOptions": { + "outDir": "build/dist", + "module": "esnext", + "target": "es5", + "lib": ["es6", "dom"], + "sourceMap": true, + "allowJs": true, + "jsx": "react-jsx", + "moduleResolution": "node", + "rootDir": "src", + "forceConsistentCasingInFileNames": true, + "noImplicitReturns": true, + "noImplicitThis": true, + "noImplicitAny": true, + "strictNullChecks": true, + "noUnusedLocals": true + } } diff --git a/packages/docs/sandboxes/dockview-app/tsconfig.json b/packages/docs/sandboxes/dockview-app/tsconfig.json index 6e13e47b5..8aebe3efe 100644 --- a/packages/docs/sandboxes/dockview-app/tsconfig.json +++ b/packages/docs/sandboxes/dockview-app/tsconfig.json @@ -1,20 +1,19 @@ { - "compilerOptions": { - "outDir": "build/dist", - "module": "esnext", - "target": "es5", - "lib": ["es6", "dom"], - "sourceMap": true, - "allowJs": true, - "jsx": "react-jsx", - "moduleResolution": "node", - "rootDir": "src", - "forceConsistentCasingInFileNames": true, - "noImplicitReturns": true, - "noImplicitThis": true, - "noImplicitAny": true, - "strictNullChecks": true, - "suppressImplicitAnyIndexErrors": true, - "noUnusedLocals": true - } + "compilerOptions": { + "outDir": "build/dist", + "module": "esnext", + "target": "es5", + "lib": ["es6", "dom"], + "sourceMap": true, + "allowJs": true, + "jsx": "react-jsx", + "moduleResolution": "node", + "rootDir": "src", + "forceConsistentCasingInFileNames": true, + "noImplicitReturns": true, + "noImplicitThis": true, + "noImplicitAny": true, + "strictNullChecks": true, + "noUnusedLocals": true + } } diff --git a/packages/docs/sandboxes/events-dockview/tsconfig.json b/packages/docs/sandboxes/events-dockview/tsconfig.json index 6e13e47b5..8aebe3efe 100644 --- a/packages/docs/sandboxes/events-dockview/tsconfig.json +++ b/packages/docs/sandboxes/events-dockview/tsconfig.json @@ -1,20 +1,19 @@ { - "compilerOptions": { - "outDir": "build/dist", - "module": "esnext", - "target": "es5", - "lib": ["es6", "dom"], - "sourceMap": true, - "allowJs": true, - "jsx": "react-jsx", - "moduleResolution": "node", - "rootDir": "src", - "forceConsistentCasingInFileNames": true, - "noImplicitReturns": true, - "noImplicitThis": true, - "noImplicitAny": true, - "strictNullChecks": true, - "suppressImplicitAnyIndexErrors": true, - "noUnusedLocals": true - } + "compilerOptions": { + "outDir": "build/dist", + "module": "esnext", + "target": "es5", + "lib": ["es6", "dom"], + "sourceMap": true, + "allowJs": true, + "jsx": "react-jsx", + "moduleResolution": "node", + "rootDir": "src", + "forceConsistentCasingInFileNames": true, + "noImplicitReturns": true, + "noImplicitThis": true, + "noImplicitAny": true, + "strictNullChecks": true, + "noUnusedLocals": true + } } diff --git a/packages/docs/sandboxes/externaldnd-dockview/tsconfig.json b/packages/docs/sandboxes/externaldnd-dockview/tsconfig.json index 6e13e47b5..8aebe3efe 100644 --- a/packages/docs/sandboxes/externaldnd-dockview/tsconfig.json +++ b/packages/docs/sandboxes/externaldnd-dockview/tsconfig.json @@ -1,20 +1,19 @@ { - "compilerOptions": { - "outDir": "build/dist", - "module": "esnext", - "target": "es5", - "lib": ["es6", "dom"], - "sourceMap": true, - "allowJs": true, - "jsx": "react-jsx", - "moduleResolution": "node", - "rootDir": "src", - "forceConsistentCasingInFileNames": true, - "noImplicitReturns": true, - "noImplicitThis": true, - "noImplicitAny": true, - "strictNullChecks": true, - "suppressImplicitAnyIndexErrors": true, - "noUnusedLocals": true - } + "compilerOptions": { + "outDir": "build/dist", + "module": "esnext", + "target": "es5", + "lib": ["es6", "dom"], + "sourceMap": true, + "allowJs": true, + "jsx": "react-jsx", + "moduleResolution": "node", + "rootDir": "src", + "forceConsistentCasingInFileNames": true, + "noImplicitReturns": true, + "noImplicitThis": true, + "noImplicitAny": true, + "strictNullChecks": true, + "noUnusedLocals": true + } } diff --git a/packages/docs/sandboxes/fullwidthtab-dockview/tsconfig.json b/packages/docs/sandboxes/fullwidthtab-dockview/tsconfig.json index 6e13e47b5..8aebe3efe 100644 --- a/packages/docs/sandboxes/fullwidthtab-dockview/tsconfig.json +++ b/packages/docs/sandboxes/fullwidthtab-dockview/tsconfig.json @@ -1,20 +1,19 @@ { - "compilerOptions": { - "outDir": "build/dist", - "module": "esnext", - "target": "es5", - "lib": ["es6", "dom"], - "sourceMap": true, - "allowJs": true, - "jsx": "react-jsx", - "moduleResolution": "node", - "rootDir": "src", - "forceConsistentCasingInFileNames": true, - "noImplicitReturns": true, - "noImplicitThis": true, - "noImplicitAny": true, - "strictNullChecks": true, - "suppressImplicitAnyIndexErrors": true, - "noUnusedLocals": true - } + "compilerOptions": { + "outDir": "build/dist", + "module": "esnext", + "target": "es5", + "lib": ["es6", "dom"], + "sourceMap": true, + "allowJs": true, + "jsx": "react-jsx", + "moduleResolution": "node", + "rootDir": "src", + "forceConsistentCasingInFileNames": true, + "noImplicitReturns": true, + "noImplicitThis": true, + "noImplicitAny": true, + "strictNullChecks": true, + "noUnusedLocals": true + } } diff --git a/packages/docs/sandboxes/groupcontrol-dockview/tsconfig.json b/packages/docs/sandboxes/groupcontrol-dockview/tsconfig.json index 6e13e47b5..8aebe3efe 100644 --- a/packages/docs/sandboxes/groupcontrol-dockview/tsconfig.json +++ b/packages/docs/sandboxes/groupcontrol-dockview/tsconfig.json @@ -1,20 +1,19 @@ { - "compilerOptions": { - "outDir": "build/dist", - "module": "esnext", - "target": "es5", - "lib": ["es6", "dom"], - "sourceMap": true, - "allowJs": true, - "jsx": "react-jsx", - "moduleResolution": "node", - "rootDir": "src", - "forceConsistentCasingInFileNames": true, - "noImplicitReturns": true, - "noImplicitThis": true, - "noImplicitAny": true, - "strictNullChecks": true, - "suppressImplicitAnyIndexErrors": true, - "noUnusedLocals": true - } + "compilerOptions": { + "outDir": "build/dist", + "module": "esnext", + "target": "es5", + "lib": ["es6", "dom"], + "sourceMap": true, + "allowJs": true, + "jsx": "react-jsx", + "moduleResolution": "node", + "rootDir": "src", + "forceConsistentCasingInFileNames": true, + "noImplicitReturns": true, + "noImplicitThis": true, + "noImplicitAny": true, + "strictNullChecks": true, + "noUnusedLocals": true + } } diff --git a/packages/docs/sandboxes/javascript/fullwidthtab-dockview/package.json b/packages/docs/sandboxes/javascript/fullwidthtab-dockview/package.json new file mode 100644 index 000000000..c4d975c1a --- /dev/null +++ b/packages/docs/sandboxes/javascript/fullwidthtab-dockview/package.json @@ -0,0 +1,27 @@ +{ + "name": "javascript-fullwidthtab-dockview", + "description": "", + "keywords": [ + "dockview" + ], + "version": "1.0.0", + "main": "src/index.ts", + "dependencies": { + "dockview-core": "*" + }, + "devDependencies": { + "typescript": "^4.9.5" + }, + "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" + ] +} \ No newline at end of file diff --git a/packages/docs/sandboxes/javascript/fullwidthtab-dockview/public/index.html b/packages/docs/sandboxes/javascript/fullwidthtab-dockview/public/index.html new file mode 100644 index 000000000..1f8a52426 --- /dev/null +++ b/packages/docs/sandboxes/javascript/fullwidthtab-dockview/public/index.html @@ -0,0 +1,44 @@ + + + + + + + + + + + + + React App + + + + +
+ + + + diff --git a/packages/docs/sandboxes/javascript/fullwidthtab-dockview/src/app.scss b/packages/docs/sandboxes/javascript/fullwidthtab-dockview/src/app.scss new file mode 100644 index 000000000..1b1438ec2 --- /dev/null +++ b/packages/docs/sandboxes/javascript/fullwidthtab-dockview/src/app.scss @@ -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); + } + } +} diff --git a/packages/docs/sandboxes/javascript/fullwidthtab-dockview/src/app.ts b/packages/docs/sandboxes/javascript/fullwidthtab-dockview/src/app.ts new file mode 100644 index 000000000..40eba745d --- /dev/null +++ b/packages/docs/sandboxes/javascript/fullwidthtab-dockview/src/app.ts @@ -0,0 +1,171 @@ +import { + IGroupPanelInitParameters, + IContentRenderer, + PanelUpdateEvent, + Parameters, + ITabRenderer, + DockviewComponent, +} from 'dockview-core'; +import './app.scss'; + +class DefaultPanel implements IContentRenderer { + private _element: HTMLElement; + private _titleElement: HTMLElement; + private _paramsElement: HTMLElement; + + get element(): HTMLElement { + return this._element; + } + + constructor() { + this._element = document.createElement('div'); + this._element.style.display = 'flex'; + this._element.style.justifyContent = 'center'; + this._element.style.alignItems = 'center'; + this._element.style.color = 'white'; + this._element.style.height = '100%'; + + this._titleElement = document.createElement('span'); + this._paramsElement = document.createElement('span'); + + this._element.appendChild(this._titleElement); + } + + init(params: IGroupPanelInitParameters): void { + this.render(params.params); + } + + update(event: PanelUpdateEvent): void { + this.render(event.params); + } + + private render(params: Record) { + this._titleElement.textContent = params.title; + + if (params.x) { + if (!this._paramsElement.parentElement) { + this._element.appendChild(this._paramsElement); + } + this._paramsElement.textContent = params.x; + } else { + this._paramsElement.parentElement?.removeChild(this._paramsElement); + } + } +} + +class DefaultTab implements ITabRenderer { + private _element: HTMLElement; + private _title: HTMLElement; + + get element(): HTMLElement { + return this._element; + } + + constructor() { + this._element = document.createElement('div'); + this._element.className = 'my-custom-tab'; + + this._title = document.createElement('span'); + + const spacer = document.createElement('span'); + spacer.style.flexGrow = '1'; + + const btn1 = document.createElement('span'); + btn1.className = 'my-custom-tab-icon material-symbols-outlined'; + btn1.textContent = 'minimize'; + + const btn2 = document.createElement('span'); + btn2.className = 'my-custom-tab-icon material-symbols-outlined'; + btn2.textContent = 'maximize'; + + const btn3 = document.createElement('span'); + btn3.className = 'my-custom-tab-icon material-symbols-outlined'; + btn3.textContent = 'close'; + + this._element.appendChild(this._title); + this._element.appendChild(spacer); + this._element.appendChild(btn1); + this._element.appendChild(btn2); + this._element.appendChild(btn3); + } + + init(params: IGroupPanelInitParameters): void { + this.render(params.params); + } + + update(event: PanelUpdateEvent): void { + this.render(event.params); + } + + private render(params: Record) { + this._title = params.title; + } +} + +export function attach(parent: HTMLElement): { + dispose: () => void; +} { + const element = document.createElement('div'); + element.className = 'dockview-theme-abyss'; + element.style.height = '100%'; + element.style.width = '100%'; + + const dockview = new DockviewComponent({ + components: { + default: DefaultPanel, + }, + tabComponents: { + default: DefaultTab, + }, + singleTabMode: 'fullwidth', + parentElement: element, + }); + + parent.appendChild(element); + + const { clientWidth, clientHeight } = parent; + dockview.layout(clientWidth, clientHeight); + + const panel1 = dockview.addPanel({ + id: 'panel_1', + component: 'default', + tabComponent: 'default', + params: { + title: 'Window 1', + }, + }); + panel1.group.locked = true; + + const panel2 = dockview.addPanel({ + id: 'panel_2', + component: 'default', + tabComponent: 'default', + params: { + title: 'Window 2', + }, + position: { + direction: 'right', + }, + }); + panel2.group.locked = true; + + const panel3 = dockview.addPanel({ + id: 'panel_3', + component: 'default', + tabComponent: 'default', + params: { + title: 'Window 3', + }, + position: { + direction: 'below', + }, + }); + panel3.group.locked = true; + + return { + dispose: () => { + dockview.dispose(); + element.remove(); + }, + }; +} diff --git a/packages/docs/sandboxes/javascript/simple-dockview/src/index.tsx b/packages/docs/sandboxes/javascript/fullwidthtab-dockview/src/index.ts similarity index 100% rename from packages/docs/sandboxes/javascript/simple-dockview/src/index.tsx rename to packages/docs/sandboxes/javascript/fullwidthtab-dockview/src/index.ts diff --git a/packages/docs/sandboxes/javascript/fullwidthtab-dockview/src/styles.css b/packages/docs/sandboxes/javascript/fullwidthtab-dockview/src/styles.css new file mode 100644 index 000000000..92b6a1b36 --- /dev/null +++ b/packages/docs/sandboxes/javascript/fullwidthtab-dockview/src/styles.css @@ -0,0 +1,16 @@ +body { + margin: 0px; + color: white; + font-family: sans-serif; + text-align: center; +} + +#root { + height: 100vh; + width: 100vw; +} + +.app { + height: 100%; + +} diff --git a/packages/docs/sandboxes/javascript/fullwidthtab-dockview/tsconfig.json b/packages/docs/sandboxes/javascript/fullwidthtab-dockview/tsconfig.json new file mode 100644 index 000000000..8aebe3efe --- /dev/null +++ b/packages/docs/sandboxes/javascript/fullwidthtab-dockview/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "outDir": "build/dist", + "module": "esnext", + "target": "es5", + "lib": ["es6", "dom"], + "sourceMap": true, + "allowJs": true, + "jsx": "react-jsx", + "moduleResolution": "node", + "rootDir": "src", + "forceConsistentCasingInFileNames": true, + "noImplicitReturns": true, + "noImplicitThis": true, + "noImplicitAny": true, + "strictNullChecks": true, + "noUnusedLocals": true + } +} diff --git a/packages/docs/sandboxes/javascript/simple-dockview/src/app.ts b/packages/docs/sandboxes/javascript/simple-dockview/src/app.ts index e368f3e42..ecce95e0e 100644 --- a/packages/docs/sandboxes/javascript/simple-dockview/src/app.ts +++ b/packages/docs/sandboxes/javascript/simple-dockview/src/app.ts @@ -2,6 +2,8 @@ import { DockviewComponent, IContentRenderer, IGroupPanelInitParameters, + PanelUpdateEvent, + Parameters, } from 'dockview-core'; class DefaultPanel implements IContentRenderer { @@ -20,6 +22,10 @@ class DefaultPanel implements IContentRenderer { init(params: IGroupPanelInitParameters): void { this._element.textContent = params.params.title; } + + update(event: PanelUpdateEvent): void { + this._element.textContent = event.params.title; + } } export function attach(parent: HTMLElement): { diff --git a/packages/docs/sandboxes/javascript/simple-dockview/src/index.ts b/packages/docs/sandboxes/javascript/simple-dockview/src/index.ts new file mode 100644 index 000000000..249b56017 --- /dev/null +++ b/packages/docs/sandboxes/javascript/simple-dockview/src/index.ts @@ -0,0 +1,10 @@ +import './styles.css'; +import 'dockview-core/dist/styles/dockview.css'; + +import { attach } from './app'; + +const rootElement = document.getElementById('root'); + +if (rootElement) { + attach(rootElement); +} diff --git a/packages/docs/sandboxes/javascript/simple-dockview/tsconfig.json b/packages/docs/sandboxes/javascript/simple-dockview/tsconfig.json index 6e13e47b5..8aebe3efe 100644 --- a/packages/docs/sandboxes/javascript/simple-dockview/tsconfig.json +++ b/packages/docs/sandboxes/javascript/simple-dockview/tsconfig.json @@ -1,20 +1,19 @@ { - "compilerOptions": { - "outDir": "build/dist", - "module": "esnext", - "target": "es5", - "lib": ["es6", "dom"], - "sourceMap": true, - "allowJs": true, - "jsx": "react-jsx", - "moduleResolution": "node", - "rootDir": "src", - "forceConsistentCasingInFileNames": true, - "noImplicitReturns": true, - "noImplicitThis": true, - "noImplicitAny": true, - "strictNullChecks": true, - "suppressImplicitAnyIndexErrors": true, - "noUnusedLocals": true - } + "compilerOptions": { + "outDir": "build/dist", + "module": "esnext", + "target": "es5", + "lib": ["es6", "dom"], + "sourceMap": true, + "allowJs": true, + "jsx": "react-jsx", + "moduleResolution": "node", + "rootDir": "src", + "forceConsistentCasingInFileNames": true, + "noImplicitReturns": true, + "noImplicitThis": true, + "noImplicitAny": true, + "strictNullChecks": true, + "noUnusedLocals": true + } } diff --git a/packages/docs/sandboxes/javascript/tabheight-dockview/package.json b/packages/docs/sandboxes/javascript/tabheight-dockview/package.json new file mode 100644 index 000000000..5aeba5d4d --- /dev/null +++ b/packages/docs/sandboxes/javascript/tabheight-dockview/package.json @@ -0,0 +1,27 @@ +{ + "name": "javascript-tabheight-dockview", + "description": "", + "keywords": [ + "dockview" + ], + "version": "1.0.0", + "main": "src/index.ts", + "dependencies": { + "dockview-core": "*" + }, + "devDependencies": { + "typescript": "^4.9.5" + }, + "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" + ] +} \ No newline at end of file diff --git a/packages/docs/sandboxes/javascript/tabheight-dockview/public/index.html b/packages/docs/sandboxes/javascript/tabheight-dockview/public/index.html new file mode 100644 index 000000000..1f8a52426 --- /dev/null +++ b/packages/docs/sandboxes/javascript/tabheight-dockview/public/index.html @@ -0,0 +1,44 @@ + + + + + + + + + + + + + React App + + + + +
+ + + + diff --git a/packages/docs/sandboxes/javascript/tabheight-dockview/src/app.scss b/packages/docs/sandboxes/javascript/tabheight-dockview/src/app.scss new file mode 100644 index 000000000..315d564ac --- /dev/null +++ b/packages/docs/sandboxes/javascript/tabheight-dockview/src/app.scss @@ -0,0 +1,3 @@ +.skinny-tabs { + --dv-tabs-and-actions-container-height: 20px; +} diff --git a/packages/docs/sandboxes/javascript/tabheight-dockview/src/app.ts b/packages/docs/sandboxes/javascript/tabheight-dockview/src/app.ts new file mode 100644 index 000000000..ca3535611 --- /dev/null +++ b/packages/docs/sandboxes/javascript/tabheight-dockview/src/app.ts @@ -0,0 +1,110 @@ +import { + DockviewComponent, + IContentRenderer, + IGroupPanelInitParameters, + PanelUpdateEvent, + Parameters, +} from 'dockview-core'; +import './app.scss'; + +class DefaultPanel implements IContentRenderer { + private _element: HTMLElement; + + get element(): HTMLElement { + return this._element; + } + + constructor() { + this._element = document.createElement('div'); + this._element.style.padding = '20px'; + this._element.style.color = 'white'; + } + + init(params: IGroupPanelInitParameters): void { + this._element.textContent = params.params.title; + } + + update(event: PanelUpdateEvent): void { + this._element.textContent = event.params.title; + } +} + +export function attach(parent: HTMLElement): { + dispose: () => void; +} { + const element = document.createElement('div'); + element.className = 'dockview-theme-abyss skinny-tabs'; + element.style.height = '100%'; + element.style.width = '100%'; + + const dockview = new DockviewComponent({ + components: { + default: DefaultPanel, + }, + parentElement: element, + }); + + parent.appendChild(element); + + const { clientWidth, clientHeight } = parent; + dockview.layout(clientWidth, clientHeight); + + dockview.addPanel({ + id: 'panel_1', + component: 'default', + params: { + title: 'Panel 1', + }, + }); + + dockview.addPanel({ + id: 'panel_2', + component: 'default', + params: { + title: 'Panel 2', + }, + }); + + dockview.addPanel({ + id: 'panel_3', + component: 'default', + params: { + title: 'Panel 3', + }, + position: { referencePanel: 'panel_1', direction: 'right' }, + }); + + dockview.addPanel({ + id: 'panel_4', + component: 'default', + params: { + title: 'Panel 4', + }, + position: { referencePanel: 'panel_3', direction: 'right' }, + }); + + dockview.addPanel({ + id: 'panel_5', + component: 'default', + params: { + title: 'Panel 5', + }, + position: { referencePanel: 'panel_4', direction: 'below' }, + }); + + dockview.addPanel({ + id: 'panel_6', + component: 'default', + params: { + title: 'Panel 6', + }, + position: { referencePanel: 'panel_5', direction: 'right' }, + }); + + return { + dispose: () => { + dockview.dispose(); + element.remove(); + }, + }; +} diff --git a/packages/docs/sandboxes/javascript/tabheight-dockview/src/index.ts b/packages/docs/sandboxes/javascript/tabheight-dockview/src/index.ts new file mode 100644 index 000000000..249b56017 --- /dev/null +++ b/packages/docs/sandboxes/javascript/tabheight-dockview/src/index.ts @@ -0,0 +1,10 @@ +import './styles.css'; +import 'dockview-core/dist/styles/dockview.css'; + +import { attach } from './app'; + +const rootElement = document.getElementById('root'); + +if (rootElement) { + attach(rootElement); +} diff --git a/packages/docs/sandboxes/javascript/tabheight-dockview/src/styles.css b/packages/docs/sandboxes/javascript/tabheight-dockview/src/styles.css new file mode 100644 index 000000000..a1d49a9b6 --- /dev/null +++ b/packages/docs/sandboxes/javascript/tabheight-dockview/src/styles.css @@ -0,0 +1,16 @@ +body { + margin: 0px; + color: white; + font-family: sans-serif; + text-align: center; +} + +#root { + height: 100vh; + width: 100vw; +} + +.app { + height: 100%; +} + diff --git a/packages/docs/sandboxes/javascript/tabheight-dockview/tsconfig.json b/packages/docs/sandboxes/javascript/tabheight-dockview/tsconfig.json new file mode 100644 index 000000000..8aebe3efe --- /dev/null +++ b/packages/docs/sandboxes/javascript/tabheight-dockview/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "outDir": "build/dist", + "module": "esnext", + "target": "es5", + "lib": ["es6", "dom"], + "sourceMap": true, + "allowJs": true, + "jsx": "react-jsx", + "moduleResolution": "node", + "rootDir": "src", + "forceConsistentCasingInFileNames": true, + "noImplicitReturns": true, + "noImplicitThis": true, + "noImplicitAny": true, + "strictNullChecks": true, + "noUnusedLocals": true + } +} diff --git a/packages/docs/sandboxes/javascript/vanilla-dockview/tsconfig.json b/packages/docs/sandboxes/javascript/vanilla-dockview/tsconfig.json index 6e13e47b5..728865f76 100644 --- a/packages/docs/sandboxes/javascript/vanilla-dockview/tsconfig.json +++ b/packages/docs/sandboxes/javascript/vanilla-dockview/tsconfig.json @@ -14,7 +14,6 @@ "noImplicitThis": true, "noImplicitAny": true, "strictNullChecks": true, - "suppressImplicitAnyIndexErrors": true, "noUnusedLocals": true } } diff --git a/packages/docs/sandboxes/layout-dockview/tsconfig.json b/packages/docs/sandboxes/layout-dockview/tsconfig.json index 6e13e47b5..8aebe3efe 100644 --- a/packages/docs/sandboxes/layout-dockview/tsconfig.json +++ b/packages/docs/sandboxes/layout-dockview/tsconfig.json @@ -1,20 +1,19 @@ { - "compilerOptions": { - "outDir": "build/dist", - "module": "esnext", - "target": "es5", - "lib": ["es6", "dom"], - "sourceMap": true, - "allowJs": true, - "jsx": "react-jsx", - "moduleResolution": "node", - "rootDir": "src", - "forceConsistentCasingInFileNames": true, - "noImplicitReturns": true, - "noImplicitThis": true, - "noImplicitAny": true, - "strictNullChecks": true, - "suppressImplicitAnyIndexErrors": true, - "noUnusedLocals": true - } + "compilerOptions": { + "outDir": "build/dist", + "module": "esnext", + "target": "es5", + "lib": ["es6", "dom"], + "sourceMap": true, + "allowJs": true, + "jsx": "react-jsx", + "moduleResolution": "node", + "rootDir": "src", + "forceConsistentCasingInFileNames": true, + "noImplicitReturns": true, + "noImplicitThis": true, + "noImplicitAny": true, + "strictNullChecks": true, + "noUnusedLocals": true + } } diff --git a/packages/docs/sandboxes/nativeapp-dockview/tsconfig.json b/packages/docs/sandboxes/nativeapp-dockview/tsconfig.json index 6e13e47b5..8aebe3efe 100644 --- a/packages/docs/sandboxes/nativeapp-dockview/tsconfig.json +++ b/packages/docs/sandboxes/nativeapp-dockview/tsconfig.json @@ -1,20 +1,19 @@ { - "compilerOptions": { - "outDir": "build/dist", - "module": "esnext", - "target": "es5", - "lib": ["es6", "dom"], - "sourceMap": true, - "allowJs": true, - "jsx": "react-jsx", - "moduleResolution": "node", - "rootDir": "src", - "forceConsistentCasingInFileNames": true, - "noImplicitReturns": true, - "noImplicitThis": true, - "noImplicitAny": true, - "strictNullChecks": true, - "suppressImplicitAnyIndexErrors": true, - "noUnusedLocals": true - } + "compilerOptions": { + "outDir": "build/dist", + "module": "esnext", + "target": "es5", + "lib": ["es6", "dom"], + "sourceMap": true, + "allowJs": true, + "jsx": "react-jsx", + "moduleResolution": "node", + "rootDir": "src", + "forceConsistentCasingInFileNames": true, + "noImplicitReturns": true, + "noImplicitThis": true, + "noImplicitAny": true, + "strictNullChecks": true, + "noUnusedLocals": true + } } diff --git a/packages/docs/sandboxes/nested-dockview/tsconfig.json b/packages/docs/sandboxes/nested-dockview/tsconfig.json index 6e13e47b5..8aebe3efe 100644 --- a/packages/docs/sandboxes/nested-dockview/tsconfig.json +++ b/packages/docs/sandboxes/nested-dockview/tsconfig.json @@ -1,20 +1,19 @@ { - "compilerOptions": { - "outDir": "build/dist", - "module": "esnext", - "target": "es5", - "lib": ["es6", "dom"], - "sourceMap": true, - "allowJs": true, - "jsx": "react-jsx", - "moduleResolution": "node", - "rootDir": "src", - "forceConsistentCasingInFileNames": true, - "noImplicitReturns": true, - "noImplicitThis": true, - "noImplicitAny": true, - "strictNullChecks": true, - "suppressImplicitAnyIndexErrors": true, - "noUnusedLocals": true - } + "compilerOptions": { + "outDir": "build/dist", + "module": "esnext", + "target": "es5", + "lib": ["es6", "dom"], + "sourceMap": true, + "allowJs": true, + "jsx": "react-jsx", + "moduleResolution": "node", + "rootDir": "src", + "forceConsistentCasingInFileNames": true, + "noImplicitReturns": true, + "noImplicitThis": true, + "noImplicitAny": true, + "strictNullChecks": true, + "noUnusedLocals": true + } } diff --git a/packages/docs/sandboxes/rendering-dockview/tsconfig.json b/packages/docs/sandboxes/rendering-dockview/tsconfig.json index 6e13e47b5..8aebe3efe 100644 --- a/packages/docs/sandboxes/rendering-dockview/tsconfig.json +++ b/packages/docs/sandboxes/rendering-dockview/tsconfig.json @@ -1,20 +1,19 @@ { - "compilerOptions": { - "outDir": "build/dist", - "module": "esnext", - "target": "es5", - "lib": ["es6", "dom"], - "sourceMap": true, - "allowJs": true, - "jsx": "react-jsx", - "moduleResolution": "node", - "rootDir": "src", - "forceConsistentCasingInFileNames": true, - "noImplicitReturns": true, - "noImplicitThis": true, - "noImplicitAny": true, - "strictNullChecks": true, - "suppressImplicitAnyIndexErrors": true, - "noUnusedLocals": true - } + "compilerOptions": { + "outDir": "build/dist", + "module": "esnext", + "target": "es5", + "lib": ["es6", "dom"], + "sourceMap": true, + "allowJs": true, + "jsx": "react-jsx", + "moduleResolution": "node", + "rootDir": "src", + "forceConsistentCasingInFileNames": true, + "noImplicitReturns": true, + "noImplicitThis": true, + "noImplicitAny": true, + "strictNullChecks": true, + "noUnusedLocals": true + } } diff --git a/packages/docs/sandboxes/resize-dockview/tsconfig.json b/packages/docs/sandboxes/resize-dockview/tsconfig.json index 6e13e47b5..8aebe3efe 100644 --- a/packages/docs/sandboxes/resize-dockview/tsconfig.json +++ b/packages/docs/sandboxes/resize-dockview/tsconfig.json @@ -1,20 +1,19 @@ { - "compilerOptions": { - "outDir": "build/dist", - "module": "esnext", - "target": "es5", - "lib": ["es6", "dom"], - "sourceMap": true, - "allowJs": true, - "jsx": "react-jsx", - "moduleResolution": "node", - "rootDir": "src", - "forceConsistentCasingInFileNames": true, - "noImplicitReturns": true, - "noImplicitThis": true, - "noImplicitAny": true, - "strictNullChecks": true, - "suppressImplicitAnyIndexErrors": true, - "noUnusedLocals": true - } + "compilerOptions": { + "outDir": "build/dist", + "module": "esnext", + "target": "es5", + "lib": ["es6", "dom"], + "sourceMap": true, + "allowJs": true, + "jsx": "react-jsx", + "moduleResolution": "node", + "rootDir": "src", + "forceConsistentCasingInFileNames": true, + "noImplicitReturns": true, + "noImplicitThis": true, + "noImplicitAny": true, + "strictNullChecks": true, + "noUnusedLocals": true + } } diff --git a/packages/docs/sandboxes/resizecontainer-dockview/tsconfig.json b/packages/docs/sandboxes/resizecontainer-dockview/tsconfig.json index 6e13e47b5..8aebe3efe 100644 --- a/packages/docs/sandboxes/resizecontainer-dockview/tsconfig.json +++ b/packages/docs/sandboxes/resizecontainer-dockview/tsconfig.json @@ -1,20 +1,19 @@ { - "compilerOptions": { - "outDir": "build/dist", - "module": "esnext", - "target": "es5", - "lib": ["es6", "dom"], - "sourceMap": true, - "allowJs": true, - "jsx": "react-jsx", - "moduleResolution": "node", - "rootDir": "src", - "forceConsistentCasingInFileNames": true, - "noImplicitReturns": true, - "noImplicitThis": true, - "noImplicitAny": true, - "strictNullChecks": true, - "suppressImplicitAnyIndexErrors": true, - "noUnusedLocals": true - } + "compilerOptions": { + "outDir": "build/dist", + "module": "esnext", + "target": "es5", + "lib": ["es6", "dom"], + "sourceMap": true, + "allowJs": true, + "jsx": "react-jsx", + "moduleResolution": "node", + "rootDir": "src", + "forceConsistentCasingInFileNames": true, + "noImplicitReturns": true, + "noImplicitThis": true, + "noImplicitAny": true, + "strictNullChecks": true, + "noUnusedLocals": true + } } diff --git a/packages/docs/sandboxes/simple-dockview/tsconfig.json b/packages/docs/sandboxes/simple-dockview/tsconfig.json index 6e13e47b5..8aebe3efe 100644 --- a/packages/docs/sandboxes/simple-dockview/tsconfig.json +++ b/packages/docs/sandboxes/simple-dockview/tsconfig.json @@ -1,20 +1,19 @@ { - "compilerOptions": { - "outDir": "build/dist", - "module": "esnext", - "target": "es5", - "lib": ["es6", "dom"], - "sourceMap": true, - "allowJs": true, - "jsx": "react-jsx", - "moduleResolution": "node", - "rootDir": "src", - "forceConsistentCasingInFileNames": true, - "noImplicitReturns": true, - "noImplicitThis": true, - "noImplicitAny": true, - "strictNullChecks": true, - "suppressImplicitAnyIndexErrors": true, - "noUnusedLocals": true - } + "compilerOptions": { + "outDir": "build/dist", + "module": "esnext", + "target": "es5", + "lib": ["es6", "dom"], + "sourceMap": true, + "allowJs": true, + "jsx": "react-jsx", + "moduleResolution": "node", + "rootDir": "src", + "forceConsistentCasingInFileNames": true, + "noImplicitReturns": true, + "noImplicitThis": true, + "noImplicitAny": true, + "strictNullChecks": true, + "noUnusedLocals": true + } } diff --git a/packages/docs/sandboxes/tabheight-dockview/tsconfig.json b/packages/docs/sandboxes/tabheight-dockview/tsconfig.json index 6e13e47b5..8aebe3efe 100644 --- a/packages/docs/sandboxes/tabheight-dockview/tsconfig.json +++ b/packages/docs/sandboxes/tabheight-dockview/tsconfig.json @@ -1,20 +1,19 @@ { - "compilerOptions": { - "outDir": "build/dist", - "module": "esnext", - "target": "es5", - "lib": ["es6", "dom"], - "sourceMap": true, - "allowJs": true, - "jsx": "react-jsx", - "moduleResolution": "node", - "rootDir": "src", - "forceConsistentCasingInFileNames": true, - "noImplicitReturns": true, - "noImplicitThis": true, - "noImplicitAny": true, - "strictNullChecks": true, - "suppressImplicitAnyIndexErrors": true, - "noUnusedLocals": true - } + "compilerOptions": { + "outDir": "build/dist", + "module": "esnext", + "target": "es5", + "lib": ["es6", "dom"], + "sourceMap": true, + "allowJs": true, + "jsx": "react-jsx", + "moduleResolution": "node", + "rootDir": "src", + "forceConsistentCasingInFileNames": true, + "noImplicitReturns": true, + "noImplicitThis": true, + "noImplicitAny": true, + "strictNullChecks": true, + "noUnusedLocals": true + } } diff --git a/packages/docs/sandboxes/updatetitle-dockview/tsconfig.json b/packages/docs/sandboxes/updatetitle-dockview/tsconfig.json index 6e13e47b5..8aebe3efe 100644 --- a/packages/docs/sandboxes/updatetitle-dockview/tsconfig.json +++ b/packages/docs/sandboxes/updatetitle-dockview/tsconfig.json @@ -1,20 +1,19 @@ { - "compilerOptions": { - "outDir": "build/dist", - "module": "esnext", - "target": "es5", - "lib": ["es6", "dom"], - "sourceMap": true, - "allowJs": true, - "jsx": "react-jsx", - "moduleResolution": "node", - "rootDir": "src", - "forceConsistentCasingInFileNames": true, - "noImplicitReturns": true, - "noImplicitThis": true, - "noImplicitAny": true, - "strictNullChecks": true, - "suppressImplicitAnyIndexErrors": true, - "noUnusedLocals": true - } + "compilerOptions": { + "outDir": "build/dist", + "module": "esnext", + "target": "es5", + "lib": ["es6", "dom"], + "sourceMap": true, + "allowJs": true, + "jsx": "react-jsx", + "moduleResolution": "node", + "rootDir": "src", + "forceConsistentCasingInFileNames": true, + "noImplicitReturns": true, + "noImplicitThis": true, + "noImplicitAny": true, + "strictNullChecks": true, + "noUnusedLocals": true + } } diff --git a/packages/docs/sandboxes/watermark-dockview/tsconfig.json b/packages/docs/sandboxes/watermark-dockview/tsconfig.json index 6e13e47b5..8aebe3efe 100644 --- a/packages/docs/sandboxes/watermark-dockview/tsconfig.json +++ b/packages/docs/sandboxes/watermark-dockview/tsconfig.json @@ -1,20 +1,19 @@ { - "compilerOptions": { - "outDir": "build/dist", - "module": "esnext", - "target": "es5", - "lib": ["es6", "dom"], - "sourceMap": true, - "allowJs": true, - "jsx": "react-jsx", - "moduleResolution": "node", - "rootDir": "src", - "forceConsistentCasingInFileNames": true, - "noImplicitReturns": true, - "noImplicitThis": true, - "noImplicitAny": true, - "strictNullChecks": true, - "suppressImplicitAnyIndexErrors": true, - "noUnusedLocals": true - } + "compilerOptions": { + "outDir": "build/dist", + "module": "esnext", + "target": "es5", + "lib": ["es6", "dom"], + "sourceMap": true, + "allowJs": true, + "jsx": "react-jsx", + "moduleResolution": "node", + "rootDir": "src", + "forceConsistentCasingInFileNames": true, + "noImplicitReturns": true, + "noImplicitThis": true, + "noImplicitAny": true, + "strictNullChecks": true, + "noUnusedLocals": true + } }