From 206255b411b22a6ddb3b51a43e31a6b7774a3396 Mon Sep 17 00:00:00 2001 From: mathuo <6710312+mathuo@users.noreply.github.com> Date: Sat, 30 Sep 2023 10:17:36 +0100 Subject: [PATCH 1/4] chore: provide additional examples --- .codesandbox/ci.json | 2 + packages/docs/docs/components/dockview.mdx | 9 + .../docs/sandboxes/ide-example/package.json | 32 ++++ .../sandboxes/ide-example/public/index.html | 44 +++++ .../docs/sandboxes/ide-example/src/app.tsx | 162 ++++++++++++++++++ .../docs/sandboxes/ide-example/src/index.tsx | 20 +++ .../docs/sandboxes/ide-example/src/styles.css | 15 ++ .../docs/sandboxes/ide-example/tsconfig.json | 18 ++ 8 files changed, 302 insertions(+) create mode 100644 packages/docs/sandboxes/ide-example/package.json create mode 100644 packages/docs/sandboxes/ide-example/public/index.html create mode 100644 packages/docs/sandboxes/ide-example/src/app.tsx create mode 100644 packages/docs/sandboxes/ide-example/src/index.tsx create mode 100644 packages/docs/sandboxes/ide-example/src/styles.css create mode 100644 packages/docs/sandboxes/ide-example/tsconfig.json diff --git a/.codesandbox/ci.json b/.codesandbox/ci.json index 986267fe3..83de42b89 100644 --- a/.codesandbox/ci.json +++ b/.codesandbox/ci.json @@ -13,6 +13,8 @@ "/packages/docs/sandboxes/externaldnd-dockview", "/packages/docs/sandboxes/floatinggroup-dockview", "/packages/docs/sandboxes/fullwidthtab-dockview", + "/packages/docs/sandboxes/headeractions-dockview", + "/packages/docs/sandboxes/ide-example", "/packages/docs/sandboxes/groupcontol-dockview", "/packages/docs/sandboxes/iframe-dockview", "/packages/docs/sandboxes/layout-dockview", diff --git a/packages/docs/docs/components/dockview.mdx b/packages/docs/docs/components/dockview.mdx index 25f67edc5..6de717337 100644 --- a/packages/docs/docs/components/dockview.mdx +++ b/packages/docs/docs/components/dockview.mdx @@ -27,6 +27,7 @@ import DockviewTabheight from '@site/sandboxes/tabheight-dockview/src/app'; import DockviewWithIFrames from '@site/sandboxes/iframe-dockview/src/app'; import DockviewFloating from '@site/sandboxes/floatinggroup-dockview/src/app'; import DockviewLockedGroup from '@site/sandboxes/lockedgroup-dockview/src/app'; +import IDEExample from '@site/sandboxes/ide-example/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'; @@ -901,6 +902,14 @@ A simple example showing events fired by `dockviewz that can be interacted with. ## Advanced Examples +## Application with sidebars + + + ### Nested Dockviews You can safely create multiple dockview instances within one page and nest dockviews within other dockviews. diff --git a/packages/docs/sandboxes/ide-example/package.json b/packages/docs/sandboxes/ide-example/package.json new file mode 100644 index 000000000..5de7b1222 --- /dev/null +++ b/packages/docs/sandboxes/ide-example/package.json @@ -0,0 +1,32 @@ +{ + "name": "ide-example", + "description": "", + "keywords": [ + "dockview" + ], + "version": "1.0.0", + "main": "src/index.tsx", + "dependencies": { + "dockview": "*", + "react": "^18.2.0", + "react-dom": "^18.2.0" + }, + "devDependencies": { + "@types/react": "^18.0.28", + "@types/react-dom": "^18.0.11", + "typescript": "^4.9.5", + "react-scripts": "*" + }, + "scripts": { + "start": "react-scripts start", + "build": "react-scripts build", + "test": "react-scripts test --env=jsdom", + "eject": "react-scripts eject" + }, + "browserslist": [ + ">0.2%", + "not dead", + "not ie <= 11", + "not op_mini all" + ] +} diff --git a/packages/docs/sandboxes/ide-example/public/index.html b/packages/docs/sandboxes/ide-example/public/index.html new file mode 100644 index 000000000..1f8a52426 --- /dev/null +++ b/packages/docs/sandboxes/ide-example/public/index.html @@ -0,0 +1,44 @@ + + + + + + + + + + + + + React App + + + + +
+ + + + diff --git a/packages/docs/sandboxes/ide-example/src/app.tsx b/packages/docs/sandboxes/ide-example/src/app.tsx new file mode 100644 index 000000000..79d5acda6 --- /dev/null +++ b/packages/docs/sandboxes/ide-example/src/app.tsx @@ -0,0 +1,162 @@ +import { + GridviewReact, + GridviewReadyEvent, + IGridviewPanelProps, + GridviewComponent, + Orientation, + GridviewApi, + LayoutPriority, +} from 'dockview'; +import * as React from 'react'; + +const components = { + 'left-sidebar': (props: IGridviewPanelProps<{ title: string }>) => { + return ( +
+ {props.params.title} +
+ ); + }, + 'middle-content': (props: IGridviewPanelProps<{ title: string }>) => { + return ( +
+ {props.params.title} +
+ ); + }, + 'right-sidebar': (props: IGridviewPanelProps<{ title: string }>) => { + return ( +
+ {props.params.title} +
+ ); + }, +}; + +const App = (props: { theme?: string }) => { + const [api, setApi] = React.useState(); + + const onReady = (event: GridviewReadyEvent) => { + event.api.fromJSON({ + grid: { + height: 1000, + width: 1000, + orientation: Orientation.HORIZONTAL, + root: { + type: 'branch', + data: [ + { + type: 'leaf', + data: { + id: 'left-sidebar-id', + component: 'left-sidebar', + snap: true, + minimumWidth: 100, + }, + size: 200, + }, + { + type: 'leaf', + data: { + id: 'middle-content-id', + component: 'middle-content', + priority: LayoutPriority.High, + minimumWidth: 100, + }, + size: 600, + }, + { + type: 'leaf', + data: { + id: 'right-sidebar-id', + component: 'right-sidebar', + snap: true, + minimumWidth: 100, + }, + size: 200, + }, + ], + }, + }, + }); + + setApi(event.api); + }; + + const onKeyPress = (event: React.KeyboardEvent) => { + if (!api) { + return; + } + + if (event.ctrlKey) { + if (event.code === 'ArrowLeft') { + const leftSidebarPanel = api.getPanel('left-sidebar-id'); + const rightSidebarPanel = api.getPanel('right-sidebar-id'); + + // const width = rightSidebarPanel?.api.width; + + if (leftSidebarPanel) { + leftSidebarPanel.api.setVisible(false); + + // if (rightSidebarPanel && typeof width === 'number') { + // rightSidebarPanel.api.setSize({ width }); + // } + } + } + } + + if (event.code === 'ArrowRight') { + const leftSidebarPanel = api.getPanel('left-sidebar-id'); + + if (leftSidebarPanel) { + leftSidebarPanel.api.setVisible(true); + // leftSidebarPanel.api.setSize({ width: 200 }); + } + } + }; + + return ( +
+
+ {'Use '} + {'Ctrl+ArrowLeft'} + {' and '} + {'Ctrl+ArrowRight'} + { + ' to show and hide the left sidebar. The right sidebar can be hidden by dragging it to the right.' + } +
+
+ +
+
+ ); +}; + +export default App; diff --git a/packages/docs/sandboxes/ide-example/src/index.tsx b/packages/docs/sandboxes/ide-example/src/index.tsx new file mode 100644 index 000000000..2fe1be232 --- /dev/null +++ b/packages/docs/sandboxes/ide-example/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/ide-example/src/styles.css b/packages/docs/sandboxes/ide-example/src/styles.css new file mode 100644 index 000000000..2198f8a37 --- /dev/null +++ b/packages/docs/sandboxes/ide-example/src/styles.css @@ -0,0 +1,15 @@ +body { + margin: 0px; + font-family: sans-serif; + text-align: center; +} + +#root { + height: 100vh; + width: 100vw; +} + +.app { + height: 100%; + +} diff --git a/packages/docs/sandboxes/ide-example/tsconfig.json b/packages/docs/sandboxes/ide-example/tsconfig.json new file mode 100644 index 000000000..cdc4fb5f5 --- /dev/null +++ b/packages/docs/sandboxes/ide-example/tsconfig.json @@ -0,0 +1,18 @@ +{ + "compilerOptions": { + "outDir": "build/dist", + "module": "esnext", + "target": "es5", + "lib": ["es6", "dom"], + "sourceMap": true, + "allowJs": true, + "jsx": "react-jsx", + "moduleResolution": "node", + "rootDir": "src", + "forceConsistentCasingInFileNames": true, + "noImplicitReturns": true, + "noImplicitThis": true, + "noImplicitAny": true, + "strictNullChecks": true + } +} From 08ec3fd3a54af6c58c68e26e353402d8748b2e26 Mon Sep 17 00:00:00 2001 From: mathuo <6710312+mathuo@users.noreply.github.com> Date: Sat, 30 Sep 2023 11:24:26 +0100 Subject: [PATCH 2/4] feat: ensure view priority used for viewChange events --- .../dockview-core/src/splitview/splitview.ts | 24 +++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/packages/dockview-core/src/splitview/splitview.ts b/packages/dockview-core/src/splitview/splitview.ts index 007a8e60a..0d56cce24 100644 --- a/packages/dockview-core/src/splitview/splitview.ts +++ b/packages/dockview-core/src/splitview/splitview.ts @@ -37,10 +37,11 @@ export interface SplitViewOptions { readonly proportionalLayout?: boolean; readonly styles?: ISplitviewStyles; } + export enum LayoutPriority { - Low = 'low', - High = 'high', - Normal = 'normal', + Low = 'low', // view is offered space last + High = 'high', // view is offered space first + Normal = 'normal', // view is offered space in view order } export interface IBaseView extends IDisposable { @@ -340,7 +341,22 @@ export class Splitview { item.size = size; - this.relayout([index]); + const indexes = range(this.viewItems.length).filter((i) => i !== index); + const lowPriorityIndexes = [ + ...indexes.filter( + (i) => this.viewItems[i].priority === LayoutPriority.Low + ), + index, + ]; + const highPriorityIndexes = indexes.filter( + (i) => this.viewItems[i].priority === LayoutPriority.High + ); + + /** + * add this view we are changing to the low-index list since we have determined the size + * here and don't want it changed + */ + this.relayout([...lowPriorityIndexes, index], highPriorityIndexes); } public addView( From 4c142b9632008728d9726d116a10de8f9811dcfa Mon Sep 17 00:00:00 2001 From: mathuo <6710312+mathuo@users.noreply.github.com> Date: Sat, 30 Sep 2023 11:31:01 +0100 Subject: [PATCH 3/4] chore: update examples --- packages/docs/sandboxes/ide-example/src/app.tsx | 8 -------- 1 file changed, 8 deletions(-) diff --git a/packages/docs/sandboxes/ide-example/src/app.tsx b/packages/docs/sandboxes/ide-example/src/app.tsx index 79d5acda6..d63b035f8 100644 --- a/packages/docs/sandboxes/ide-example/src/app.tsx +++ b/packages/docs/sandboxes/ide-example/src/app.tsx @@ -109,16 +109,9 @@ const App = (props: { theme?: string }) => { if (event.ctrlKey) { if (event.code === 'ArrowLeft') { const leftSidebarPanel = api.getPanel('left-sidebar-id'); - const rightSidebarPanel = api.getPanel('right-sidebar-id'); - - // const width = rightSidebarPanel?.api.width; if (leftSidebarPanel) { leftSidebarPanel.api.setVisible(false); - - // if (rightSidebarPanel && typeof width === 'number') { - // rightSidebarPanel.api.setSize({ width }); - // } } } } @@ -128,7 +121,6 @@ const App = (props: { theme?: string }) => { if (leftSidebarPanel) { leftSidebarPanel.api.setVisible(true); - // leftSidebarPanel.api.setSize({ width: 200 }); } } }; From fc1c747bed61ac4f169ed5c04695b4eb940eab14 Mon Sep 17 00:00:00 2001 From: mathuo <6710312+mathuo@users.noreply.github.com> Date: Sat, 30 Sep 2023 11:47:36 +0100 Subject: [PATCH 4/4] test: add tests --- .../src/__tests__/splitview/splitview.spec.ts | 96 +++++++++++++++++++ 1 file changed, 96 insertions(+) diff --git a/packages/dockview-core/src/__tests__/splitview/splitview.spec.ts b/packages/dockview-core/src/__tests__/splitview/splitview.spec.ts index ec7654393..ed02acf4c 100644 --- a/packages/dockview-core/src/__tests__/splitview/splitview.spec.ts +++ b/packages/dockview-core/src/__tests__/splitview/splitview.spec.ts @@ -676,4 +676,100 @@ describe('splitview', () => { expect(addEventListenerSpy).toBeCalledTimes(3); expect(removeEventListenerSpy).toBeCalledTimes(3); }); + + test('setViewVisible', () => { + const splitview = new Splitview(container, { + orientation: Orientation.HORIZONTAL, + proportionalLayout: false, + }); + splitview.layout(900, 500); + + const view1 = new Testview(0, 1000); + const view2 = new Testview(0, 1000); + const view3 = new Testview(0, 1000); + + splitview.addView(view1); + splitview.addView(view2); + splitview.addView(view3); + + expect([view1.size, view2.size, view3.size]).toEqual([300, 300, 300]); + + splitview.setViewVisible(0, false); + expect([view1.size, view2.size, view3.size]).toEqual([0, 300, 600]); + + splitview.setViewVisible(0, true); + expect([view1.size, view2.size, view3.size]).toEqual([300, 300, 300]); + }); + + test('setViewVisible with one view having high layout priority', () => { + const splitview = new Splitview(container, { + orientation: Orientation.HORIZONTAL, + proportionalLayout: false, + }); + splitview.layout(900, 500); + + const view1 = new Testview(0, 1000); + const view2 = new Testview(0, 1000, LayoutPriority.High); + const view3 = new Testview(0, 1000); + + splitview.addView(view1); + splitview.addView(view2); + splitview.addView(view3); + + expect([view1.size, view2.size, view3.size]).toEqual([300, 300, 300]); + + splitview.setViewVisible(0, false); + expect([view1.size, view2.size, view3.size]).toEqual([0, 600, 300]); + + splitview.setViewVisible(0, true); + expect([view1.size, view2.size, view3.size]).toEqual([300, 300, 300]); + }); + + test('set view size', () => { + const splitview = new Splitview(container, { + orientation: Orientation.HORIZONTAL, + proportionalLayout: false, + }); + splitview.layout(900, 500); + + const view1 = new Testview(0, 1000); + const view2 = new Testview(0, 1000); + const view3 = new Testview(0, 1000); + + splitview.addView(view1); + splitview.addView(view2); + splitview.addView(view3); + + expect([view1.size, view2.size, view3.size]).toEqual([300, 300, 300]); + + view1.fireChangeEvent({ size: 0 }); + expect([view1.size, view2.size, view3.size]).toEqual([0, 300, 600]); + + view1.fireChangeEvent({ size: 300 }); + expect([view1.size, view2.size, view3.size]).toEqual([300, 300, 300]); + }); + + test('set view size with one view having high layout priority', () => { + const splitview = new Splitview(container, { + orientation: Orientation.HORIZONTAL, + proportionalLayout: false, + }); + splitview.layout(900, 500); + + const view1 = new Testview(0, 1000); + const view2 = new Testview(0, 1000, LayoutPriority.High); + const view3 = new Testview(0, 1000); + + splitview.addView(view1); + splitview.addView(view2); + splitview.addView(view3); + + expect([view1.size, view2.size, view3.size]).toEqual([300, 300, 300]); + + view1.fireChangeEvent({ size: 0 }); + expect([view1.size, view2.size, view3.size]).toEqual([0, 600, 300]); + + view1.fireChangeEvent({ size: 300 }); + expect([view1.size, view2.size, view3.size]).toEqual([300, 300, 300]); + }); });