Merge branch 'master' of https://github.com/mathuo/dockview into 230-explore-floating-groups

This commit is contained in:
mathuo 2023-05-31 20:02:45 +01:00
commit 7fdede6952
No known key found for this signature in database
GPG Key ID: C6EEDEFD6CA07281
139 changed files with 2297 additions and 782 deletions

View File

@ -20,9 +20,13 @@
"/packages/docs/sandboxes/resize-dockview", "/packages/docs/sandboxes/resize-dockview",
"/packages/docs/sandboxes/resizecontainer-dockview", "/packages/docs/sandboxes/resizecontainer-dockview",
"/packages/docs/sandboxes/simple-dockview", "/packages/docs/sandboxes/simple-dockview",
"/packages/docs/sandboxes/tabheight-dockview",
"/packages/docs/sandboxes/updatetitle-dockview", "/packages/docs/sandboxes/updatetitle-dockview",
"/packages/docs/sandboxes/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" "node": "16"
} }

33
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@ -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.

View File

@ -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.

View File

@ -33,8 +33,10 @@ jobs:
working-directory: packages/dockview working-directory: packages/dockview
- run: npm run build - run: npm run build
working-directory: packages/docs working-directory: packages/docs
- run: npm run deploy-docs - run: npm run docs
working-directory: packages/docs working-directory: .
- run: npm run package-docs
working-directory: .
- name: Deploy 🚀 - name: Deploy 🚀
uses: JamesIves/github-pages-deploy-action@3.7.1 uses: JamesIves/github-pages-deploy-action@3.7.1
with: with:

1
.gitignore vendored
View File

@ -13,3 +13,4 @@ test-report.xml
*.code-workspace *.code-workspace
yarn-error.log yarn-error.log
/build /build
/docs/

View File

@ -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 ## 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 - Tabular docking and Drag and Drop support
- Documentation and examples - 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 ## 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 npm install --save dockview

View File

@ -3,7 +3,7 @@
"packages/*" "packages/*"
], ],
"useWorkspaces": true, "useWorkspaces": true,
"version": "1.7.0", "version": "1.7.2",
"npmClient": "yarn", "npmClient": "yarn",
"command": { "command": {
"publish": { "publish": {

View File

@ -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",
]
}
}

View File

@ -19,7 +19,9 @@
"bootstrap": "lerna bootstrap", "bootstrap": "lerna bootstrap",
"test:cov": "jest --coverage", "test:cov": "jest --coverage",
"version-beta-build": "lerna version prerelease --preid beta", "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": { "repository": {
"type": "git", "type": "git",
@ -56,6 +58,7 @@
"ts-jest": "^29.0.5", "ts-jest": "^29.0.5",
"ts-loader": "^9.4.2", "ts-loader": "^9.4.2",
"tslib": "^2.5.0", "tslib": "^2.5.0",
"typedoc": "^0.24.7",
"typescript": "^4.9.5", "typescript": "^4.9.5",
"webpack": "^5.75.0", "webpack": "^5.75.0",
"webpack-cli": "^5.0.1", "webpack-cli": "^5.0.1",

View File

@ -1,7 +1,7 @@
<div align="center"> <div align="center">
<h1>dockview</h1> <h1>dockview</h1>
<p>Zero dependency layout manager supporting tabs, grids and splitviews with ReactJS support written in TypeScript</p> <p>Zero dependency layout manager supporting tabs, grids and splitviews written in TypeScript</p>
</div> </div>
@ -17,8 +17,6 @@
Please see the website: https://dockview.dev Please see the website: https://dockview.dev
Want to inspect the latest deployment? Go to https://unpkg.com/browse/dockview@latest/
## Features ## Features
- Simple splitviews, nested splitviews (i.e. gridviews) supporting full layout managment with - 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 - Tabular docking and Drag and Drop support
- Documentation and examples - 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 ## 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 ```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: You should also attach a dockview theme to an element containing your components. For example:

View File

@ -20,6 +20,14 @@ const config: JestConfigWithTsJest = {
coverageDirectory: '<rootDir>/packages/dockview-core/coverage/', coverageDirectory: '<rootDir>/packages/dockview-core/coverage/',
testResultsProcessor: 'jest-sonar-reporter', testResultsProcessor: 'jest-sonar-reporter',
testEnvironment: 'jsdom', testEnvironment: 'jsdom',
transform: {
'^.+\\.tsx?$': [
'ts-jest',
{
tsconfig: '<rootDir>/tsconfig.test.json',
},
],
},
}; };
export default config; export default config;

View File

@ -1,6 +1,6 @@
{ {
"name": "dockview-core", "name": "dockview-core",
"version": "1.7.0", "version": "1.7.2",
"description": "Zero dependency layout manager supporting tabs, grids and splitviews with ReactJS support", "description": "Zero dependency layout manager supporting tabs, grids and splitviews with ReactJS support",
"main": "./dist/cjs/index.js", "main": "./dist/cjs/index.js",
"types": "./dist/cjs/index.d.ts", "types": "./dist/cjs/index.d.ts",
@ -20,7 +20,7 @@
"build:esm": "cross-env ../../node_modules/.bin/tsc --project ./tsconfig.esm.json --extendedDiagnostics", "build:esm": "cross-env ../../node_modules/.bin/tsc --project ./tsconfig.esm.json --extendedDiagnostics",
"build:modulefiles": "rollup -c", "build:modulefiles": "rollup -c",
"build": "npm run build:ci && npm run build:modulefiles", "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", "prepublishOnly": "npm run rebuild && npm run test",
"docs": "typedoc", "docs": "typedoc",
"rebuild": "npm run clean && npm run build", "rebuild": "npm run clean && npm run build",

View File

@ -57,9 +57,13 @@ function createBundle(format, options) {
].join('\n'), ].join('\n'),
}; };
const plugins = [ const plugins = [
typescript({ typescript({
tsconfig: 'tsconfig.esm.json', tsconfig: 'tsconfig.esm.json',
compilerOptions: {
declaration: false,
},
}), }),
]; ];

View File

@ -473,7 +473,6 @@ describe('dockviewComponent', () => {
title: 'panel5', title: 'panel5',
}, },
}, },
options: { tabHeight: 25 },
}); });
expect(JSON.parse(JSON.stringify(dockview.toJSON()))).toEqual({ expect(JSON.parse(JSON.stringify(dockview.toJSON()))).toEqual({
@ -558,7 +557,6 @@ describe('dockviewComponent', () => {
title: 'panel5', title: 'panel5',
}, },
}, },
options: { tabHeight: 25 },
}); });
}); });
@ -989,7 +987,6 @@ describe('dockviewComponent', () => {
title: 'view_3_title', title: 'view_3_title',
}, },
}, },
options: {},
}); });
expect(removedGroups.length).toBe(2); expect(removedGroups.length).toBe(2);
@ -1512,7 +1509,6 @@ describe('dockviewComponent', () => {
title: 'panel5', title: 'panel5',
}, },
}, },
options: { tabHeight: 25 },
}); });
jest.runAllTimers(); jest.runAllTimers();
@ -1547,7 +1543,6 @@ describe('dockviewComponent', () => {
orientation: Orientation.VERTICAL, orientation: Orientation.VERTICAL,
}, },
panels: {}, panels: {},
options: { tabHeight: 25 },
}); });
jest.runAllTimers(); jest.runAllTimers();
@ -1647,7 +1642,6 @@ describe('dockviewComponent', () => {
title: 'panel5', title: 'panel5',
}, },
}, },
options: { tabHeight: 25 },
}); });
}); });
@ -1709,7 +1703,6 @@ describe('dockviewComponent', () => {
title: 'panel2', title: 'panel2',
}, },
}, },
options: { tabHeight: 25 },
}); });
expect(JSON.parse(JSON.stringify(dockview.toJSON()))).toEqual({ expect(JSON.parse(JSON.stringify(dockview.toJSON()))).toEqual({
@ -1756,7 +1749,6 @@ describe('dockviewComponent', () => {
title: 'panel2', title: 'panel2',
}, },
}, },
options: { tabHeight: 25 },
}); });
}); });
@ -1822,7 +1814,6 @@ describe('dockviewComponent', () => {
title: 'panel3', title: 'panel3',
}, },
}, },
options: { tabHeight: 25 },
}); });
const group = dockview.getGroupPanel('panel2')!.api.group; const group = dockview.getGroupPanel('panel2')!.api.group;
@ -1928,7 +1919,7 @@ describe('dockviewComponent', () => {
id: 'group-1', id: 'group-1',
activeView: 'panel1', activeView: 'panel1',
}, },
size: 1000, size: 500,
}, },
], ],
size: 1000, size: 1000,
@ -1949,7 +1940,6 @@ describe('dockviewComponent', () => {
title: 'panel2', title: 'panel2',
}, },
}, },
options: {},
}); });
}); });
@ -2090,7 +2080,6 @@ describe('dockviewComponent', () => {
title: 'panel3', title: 'panel3',
}, },
}, },
options: {},
}); });
}); });
@ -2218,7 +2207,6 @@ describe('dockviewComponent', () => {
title: 'panel3', title: 'panel3',
}, },
}, },
options: {},
}); });
}); });
@ -2304,7 +2292,6 @@ describe('dockviewComponent', () => {
size: 0, size: 0,
}, },
}, },
options: {},
panels: {}, panels: {},
}); });
}); });

View File

@ -4,27 +4,39 @@ import {
} from '../../dockview/dockviewComponent'; } from '../../dockview/dockviewComponent';
import { DockviewPanelModel } from '../../dockview/dockviewPanelModel'; import { DockviewPanelModel } from '../../dockview/dockviewPanelModel';
import { IContentRenderer, ITabRenderer } from '../../dockview/types'; import { IContentRenderer, ITabRenderer } from '../../dockview/types';
import { GroupPanelFrameworkComponentFactory } from '../../dockview/options';
import { DefaultTab } from '../../dockview/components/tab/defaultTab';
describe('dockviewGroupPanel', () => { describe('dockviewGroupPanel', () => {
test('that dispose is called on content and tab renderers when present', () => { let contentMock: jest.Mock<IContentRenderer>;
const contentMock = jest.fn<IContentRenderer, []>(() => { let tabMock: jest.Mock<ITabRenderer>;
let accessorMock: jest.Mock<IDockviewComponent>;
beforeEach(() => {
contentMock = jest.fn<IContentRenderer, []>(() => {
const partial: Partial<IContentRenderer> = { const partial: Partial<IContentRenderer> = {
element: document.createElement('div'), element: document.createElement('div'),
dispose: jest.fn(), dispose: jest.fn(),
update: jest.fn(),
onGroupChange: jest.fn(),
onPanelVisibleChange: jest.fn(),
}; };
return partial as IContentRenderer; return partial as IContentRenderer;
}); });
const tabMock = jest.fn<ITabRenderer, []>(() => { tabMock = jest.fn<ITabRenderer, []>(() => {
const partial: Partial<IContentRenderer> = { const partial: Partial<IContentRenderer> = {
element: document.createElement('div'), element: document.createElement('div'),
dispose: jest.fn(), dispose: jest.fn(),
update: jest.fn(),
onGroupChange: jest.fn(),
onPanelVisibleChange: jest.fn(),
}; };
return partial as IContentRenderer; return partial as IContentRenderer;
}); });
const accessorMock = jest.fn<Partial<DockviewComponent>, []>(() => { accessorMock = jest.fn<DockviewComponent, []>(() => {
return { const partial: Partial<DockviewComponent> = {
options: { options: {
components: { components: {
contentComponent: contentMock, 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( const cut = new DockviewPanelModel(
<IDockviewComponent>new accessorMock(), <IDockviewComponent>new accessorMock(),
'id', 'id',
@ -50,34 +66,6 @@ describe('dockviewGroupPanel', () => {
}); });
test('that update is called on content and tab renderers when present', () => { test('that update is called on content and tab renderers when present', () => {
const contentMock = jest.fn<IContentRenderer, []>(() => {
const partial: Partial<IContentRenderer> = {
element: document.createElement('div'),
update: jest.fn(),
};
return partial as IContentRenderer;
});
const tabMock = jest.fn<ITabRenderer, []>(() => {
const partial: Partial<IContentRenderer> = {
element: document.createElement('div'),
update: jest.fn(),
};
return partial as IContentRenderer;
});
const accessorMock = jest.fn<Partial<DockviewComponent>, []>(() => {
return {
options: {
components: {
contentComponent: contentMock,
},
tabComponents: {
tabComponent: tabMock,
},
},
};
});
const cut = new DockviewPanelModel( const cut = new DockviewPanelModel(
<IDockviewComponent>new accessorMock(), <IDockviewComponent>new accessorMock(),
'id', 'id',
@ -94,36 +82,6 @@ describe('dockviewGroupPanel', () => {
}); });
test('that events are fired', () => { test('that events are fired', () => {
const contentMock = jest.fn<IContentRenderer, []>(() => {
const partial: Partial<IContentRenderer> = {
element: document.createElement('div'),
onGroupChange: jest.fn(),
onPanelVisibleChange: jest.fn(),
};
return partial as IContentRenderer;
});
const tabMock = jest.fn<ITabRenderer, []>(() => {
const partial: Partial<IContentRenderer> = {
element: document.createElement('div'),
onGroupChange: jest.fn(),
onPanelVisibleChange: jest.fn(),
};
return partial as IContentRenderer;
});
const accessorMock = jest.fn<Partial<DockviewComponent>, []>(() => {
return {
options: {
components: {
contentComponent: contentMock,
},
tabComponents: {
tabComponent: tabMock,
},
},
};
});
const cut = new DockviewPanelModel( const cut = new DockviewPanelModel(
<IDockviewComponent>new accessorMock(), <IDockviewComponent>new accessorMock(),
'id', 'id',
@ -168,4 +126,176 @@ describe('dockviewGroupPanel', () => {
expect(cut.content.onPanelVisibleChange).toHaveBeenCalledTimes(2); expect(cut.content.onPanelVisibleChange).toHaveBeenCalledTimes(2);
expect(cut.tab.onPanelVisibleChange).toHaveBeenCalledTimes(2); expect(cut.tab.onPanelVisibleChange).toHaveBeenCalledTimes(2);
}); });
test('that the default tab is created', () => {
accessorMock = jest.fn<DockviewComponent, []>(() => {
const partial: Partial<DockviewComponent> = {
options: {
components: {
contentComponent: contentMock,
},
tabComponents: {
tabComponent: jest
.fn()
.mockImplementation(() => tabMock),
},
},
};
return partial as DockviewComponent;
});
const cut = new DockviewPanelModel(
<IDockviewComponent>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<DockviewComponent, []>(() => {
const partial: Partial<DockviewComponent> = {
options: {
components: {
contentComponent: contentMock,
},
tabComponents: {
tabComponent: jest
.fn()
.mockImplementation(() => tabMock),
},
defaultTabComponent: 'tabComponent',
},
};
return partial as DockviewComponent;
});
const cut = new DockviewPanelModel(
<IDockviewComponent>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<DockviewComponent, []>(() => {
const partial: Partial<DockviewComponent> = {
options: {
components: {
contentComponent: contentMock,
},
frameworkTabComponents: {
tabComponent: tabMock,
},
frameworkComponentFactory: (<
Partial<GroupPanelFrameworkComponentFactory>
>{
tab: { createComponent: tabFactory },
}) as GroupPanelFrameworkComponentFactory,
},
};
return partial as DockviewComponent;
});
const cut = new DockviewPanelModel(
<IDockviewComponent>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<DockviewComponent, []>(() => {
const partial: Partial<DockviewComponent> = {
options: {
components: {
contentComponent: contentMock,
},
},
};
return partial as DockviewComponent;
});
const cut = new DockviewPanelModel(
<IDockviewComponent>new accessorMock(),
'id',
'contentComponent'
);
expect(cut.tab instanceof DefaultTab).toBeTruthy();
});
test('that the default content is created', () => {
accessorMock = jest.fn<DockviewComponent, []>(() => {
const partial: Partial<DockviewComponent> = {
options: {
components: {
contentComponent: jest.fn().mockImplementation(() => {
return contentMock;
}),
},
},
};
return partial as DockviewComponent;
});
const cut = new DockviewPanelModel(
<IDockviewComponent>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<DockviewComponent, []>(() => {
const partial: Partial<DockviewComponent> = {
options: {
frameworkComponents: {
contentComponent: contentMock,
},
frameworkComponentFactory: (<
Partial<GroupPanelFrameworkComponentFactory>
>{
content: { createComponent: contentFactory },
}) as GroupPanelFrameworkComponentFactory,
},
};
return partial as DockviewComponent;
});
const cut = new DockviewPanelModel(
<IDockviewComponent>new accessorMock(),
'id',
'contentComponent'
);
expect(contentFactory).toHaveBeenCalledWith(
'id',
'contentComponent',
contentMock
);
expect(cut.content).toEqual(content);
});
}); });

View File

@ -114,7 +114,7 @@ describe('baseComponentGridview', () => {
proportionalLayout: true, proportionalLayout: true,
}); });
const events: TestPanel[] = []; const events: (TestPanel | undefined)[] = [];
const disposable = new CompositeDisposable( const disposable = new CompositeDisposable(
cut.onDidAddGroup((event) => { cut.onDidAddGroup((event) => {

View File

@ -1,5 +1,31 @@
import { Gridview } from '../../gridview/gridview'; import { Emitter, Event } from '../../events';
import { Orientation } from '../../splitview/splitview'; 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<IViewSize | undefined> = new Emitter<
IViewSize | undefined
>().event;
element: HTMLElement = document.createElement('div');
layout(width: number, height: number): void {
//
}
toJSON(): object {
return {};
}
}
describe('gridview', () => { describe('gridview', () => {
let container: HTMLElement; let container: HTMLElement;
@ -25,4 +51,69 @@ describe('gridview', () => {
expect(container.childNodes.length).toBe(0); 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);
});
}); });

View File

@ -18,11 +18,9 @@ import { LocalSelectionTransfer, PanelTransfer } from '../../dnd/dataTransfer';
import { CompositeDisposable } from '../../lifecycle'; import { CompositeDisposable } from '../../lifecycle';
import { DockviewPanelApi } from '../../api/dockviewPanelApi'; import { DockviewPanelApi } from '../../api/dockviewPanelApi';
import { IDockviewPanel } from '../../dockview/dockviewPanel'; import { IDockviewPanel } from '../../dockview/dockviewPanel';
import { import { IDockviewPanelModel } from '../../dockview/dockviewPanelModel';
IDockviewPanelModel,
DockviewPanelModel,
} from '../../dockview/dockviewPanelModel';
import { DockviewGroupPanel } from '../../dockview/dockviewGroupPanel'; import { DockviewGroupPanel } from '../../dockview/dockviewGroupPanel';
import { WatermarkRendererInitParameters } from '../../dockview/types';
enum GroupChangeKind2 { enum GroupChangeKind2 {
ADD_PANEL, ADD_PANEL,
@ -76,7 +74,7 @@ class Watermark implements IWatermarkRenderer {
return 'watermark-id'; return 'watermark-id';
} }
init(params: GroupPanelPartInitParameters) { init(params: WatermarkRendererInitParameters) {
// //
} }
@ -170,7 +168,7 @@ class TestHeaderPart implements ITabRenderer {
export class TestPanel implements IDockviewPanel { export class TestPanel implements IDockviewPanel {
private _group: DockviewGroupPanel | undefined; private _group: DockviewGroupPanel | undefined;
private _params: IGroupPanelInitParameters; private _params: IGroupPanelInitParameters | undefined;
readonly view: IDockviewPanelModel; readonly view: IDockviewPanelModel;
get title() { get title() {
@ -237,6 +235,8 @@ describe('groupview', () => {
removePanelMock = jest.fn(); removePanelMock = jest.fn();
removeGroupMock = jest.fn(); removeGroupMock = jest.fn();
options = {};
dockview = (<Partial<DockviewComponent>>{ dockview = (<Partial<DockviewComponent>>{
options: {}, options: {},
createWatermarkComponent: () => new Watermark(), createWatermarkComponent: () => new Watermark(),
@ -248,9 +248,6 @@ describe('groupview', () => {
onDidRemovePanel: jest.fn(), onDidRemovePanel: jest.fn(),
}) as DockviewComponent; }) as DockviewComponent;
options = {
tabHeight: 30,
};
groupview = new DockviewGroupPanel(dockview, 'groupview-1', options); groupview = new DockviewGroupPanel(dockview, 'groupview-1', options);
groupview.initialize(); groupview.initialize();
}); });
@ -261,7 +258,6 @@ describe('groupview', () => {
const panel3 = new TestPanel('panel3', jest.fn() as any); const panel3 = new TestPanel('panel3', jest.fn() as any);
const groupview2 = new DockviewGroupPanel(dockview, 'groupview-2', { const groupview2 = new DockviewGroupPanel(dockview, 'groupview-2', {
tabHeight: 25,
panels: [panel1, panel2, panel3], panels: [panel1, panel2, panel3],
activePanel: panel2, activePanel: panel2,
}); });
@ -518,7 +514,7 @@ describe('groupview', () => {
dockviewComponent, dockviewComponent,
'id', 'id',
{}, {},
null null as any
); );
expect(cut.toJSON()).toEqual({ expect(cut.toJSON()).toEqual({
@ -541,7 +537,7 @@ describe('groupview', () => {
dockviewComponent, dockviewComponent,
'id', 'id',
{}, {},
null null as any
); );
cut.locked = true; cut.locked = true;
@ -570,25 +566,25 @@ describe('groupview', () => {
dockviewComponent, dockviewComponent,
'id', 'id',
{}, {},
null null as any
); );
const contentContainer = groupviewContainer const contentContainer = groupviewContainer
.getElementsByClassName('content-container') .getElementsByClassName('content-container')
.item(0)!.childNodes; .item(0)!.childNodes;
const panel1 = new TestPanel('id_1', null); const panel1 = new TestPanel('id_1', null as any);
cut.openPanel(panel1); cut.openPanel(panel1);
expect(contentContainer.length).toBe(1); expect(contentContainer.length).toBe(1);
expect(contentContainer.item(0)).toBe(panel1.view.content.element); 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); cut.openPanel(panel2);
expect(contentContainer.length).toBe(1); expect(contentContainer.length).toBe(1);
expect(contentContainer.item(0)).toBe(panel2.view.content.element); 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 }); cut.openPanel(panel3, { skipSetPanelActive: true });
expect(contentContainer.length).toBe(1); expect(contentContainer.length).toBe(1);

View File

@ -423,14 +423,6 @@ export class DockviewApi implements CommonApi<SerializedDockview> {
constructor(private readonly component: IDockviewComponent) {} constructor(private readonly component: IDockviewComponent) {}
getTabHeight(): number | undefined {
return this.component.tabHeight;
}
setTabHeight(height: number | undefined): void {
this.component.tabHeight = height;
}
focus(): void { focus(): void {
this.component.focus(); this.component.focus();
} }

View File

@ -20,7 +20,6 @@ export interface ITabsContainer extends IDisposable {
readonly element: HTMLElement; readonly element: HTMLElement;
readonly panels: string[]; readonly panels: string[];
readonly size: number; readonly size: number;
height: number | undefined;
delete: (id: string) => void; delete: (id: string) => void;
indexOf: (id: string) => number; indexOf: (id: string) => number;
onDrop: Event<TabDropIndexEvent>; onDrop: Event<TabDropIndexEvent>;
@ -48,7 +47,6 @@ export class TabsContainer
private selectedIndex = -1; private selectedIndex = -1;
private actions: HTMLElement | undefined; private actions: HTMLElement | undefined;
private _height: number | undefined;
private _hidden = false; private _hidden = false;
private readonly _onDrop = new Emitter<TabDropIndexEvent>(); private readonly _onDrop = new Emitter<TabDropIndexEvent>();
@ -62,24 +60,6 @@ export class TabsContainer
return this.tabs.length; 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 { get hidden(): boolean {
return this._hidden; return this._hidden;
} }
@ -139,8 +119,6 @@ export class TabsContainer
this._element = document.createElement('div'); this._element = document.createElement('div');
this._element.className = 'tabs-and-actions-container'; this._element.className = 'tabs-and-actions-container';
this.height = accessor.options.tabHeight;
toggleClass( toggleClass(
this._element, this._element,
'dv-full-width-single-tab', 'dv-full-width-single-tab',

View File

@ -1,9 +1,7 @@
import { GroupviewPanelState, ITabRenderer } from './types'; import { GroupviewPanelState } from './types';
import { DockviewGroupPanel } from './dockviewGroupPanel'; import { DockviewGroupPanel } from './dockviewGroupPanel';
import { DockviewPanel, IDockviewPanel } from './dockviewPanel'; import { DockviewPanel, IDockviewPanel } from './dockviewPanel';
import { IDockviewComponent } from './dockviewComponent'; import { IDockviewComponent } from './dockviewComponent';
import { createComponent } from '../panel/componentFactory';
import { DefaultTab } from './components/tab/defaultTab';
import { DockviewPanelModel } from './dockviewPanelModel'; import { DockviewPanelModel } from './dockviewPanelModel';
import { DockviewApi } from '../api/component.api'; import { DockviewApi } from '../api/component.api';
@ -14,7 +12,7 @@ export interface IPanelDeserializer {
): IDockviewPanel; ): IDockviewPanel;
} }
// depreciated // @depreciated
interface LegacyState extends GroupviewPanelState { interface LegacyState extends GroupviewPanelState {
view?: { view?: {
tab?: { id: string }; tab?: { id: string };
@ -42,30 +40,6 @@ export class DefaultDockviewDeserialzier implements IPanelDeserializer {
? viewData.tab?.id ? viewData.tab?.id
: panelData.tabComponent; : 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( const view = new DockviewPanelModel(
this.layout, this.layout,
panelId, panelId,

View File

@ -60,7 +60,6 @@ export interface SerializedDockview {
}; };
panels: { [key: string]: GroupviewPanelState }; panels: { [key: string]: GroupviewPanelState };
activeGroup?: string; activeGroup?: string;
options?: { tabHeight?: number };
} }
export type DockviewComponentUpdateOptions = Pick< export type DockviewComponentUpdateOptions = Pick<
@ -87,7 +86,6 @@ export interface IDockviewComponent extends IBaseGrid<DockviewGroupPanel> {
readonly panels: IDockviewPanel[]; readonly panels: IDockviewPanel[];
readonly onDidDrop: Event<DockviewDropEvent>; readonly onDidDrop: Event<DockviewDropEvent>;
readonly orientation: Orientation; readonly orientation: Orientation;
tabHeight: number | undefined;
updateOptions(options: DockviewComponentUpdateOptions): void; updateOptions(options: DockviewComponentUpdateOptions): void;
moveGroupOrPanel( moveGroupOrPanel(
referenceGroup: DockviewGroupPanel, referenceGroup: DockviewGroupPanel,
@ -176,17 +174,6 @@ export class DockviewComponent
return activeGroup.activePanel; 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) { constructor(options: DockviewComponentOptions) {
super({ super({
proportionalLayout: true, proportionalLayout: true,
@ -417,18 +404,13 @@ export class DockviewComponent
grid: data, grid: data,
panels, panels,
activeGroup: this.activeGroup?.id, activeGroup: this.activeGroup?.id,
options: { tabHeight: this.tabHeight },
}; };
} }
fromJSON(data: SerializedDockview): void { fromJSON(data: SerializedDockview): void {
this.clear(); this.clear();
const { grid, panels, options, activeGroup } = data; const { grid, panels, activeGroup } = data;
if (typeof options?.tabHeight === 'number') {
this.tabHeight = options.tabHeight;
}
if (grid.root.type !== 'branch' || !Array.isArray(grid.root.data)) { if (grid.root.type !== 'branch' || !Array.isArray(grid.root.data)) {
throw new Error('root must be of type branch'); throw new Error('root must be of type branch');
@ -873,10 +855,7 @@ export class DockviewComponent
createGroup(options?: GroupOptions): DockviewGroupPanel { createGroup(options?: GroupOptions): DockviewGroupPanel {
if (!options) { if (!options) {
options = { tabHeight: this.tabHeight }; options = {};
}
if (typeof options.tabHeight !== 'number') {
options.tabHeight = this.tabHeight;
} }
let id = options?.id; let id = options?.id;
@ -929,10 +908,6 @@ export class DockviewComponent
// not an ideal pattern // not an ideal pattern
view.initialize(); view.initialize();
if (typeof this.options.tabHeight === 'number') {
view.model.header.height = this.options.tabHeight;
}
return view; return view;
} }

View File

@ -56,7 +56,6 @@ export interface GroupOptions extends CoreGroupOptions {
readonly panels?: IDockviewPanel[]; readonly panels?: IDockviewPanel[];
readonly activePanel?: IDockviewPanel; readonly activePanel?: IDockviewPanel;
readonly id?: string; readonly id?: string;
tabHeight?: number;
} }
export interface GroupPanelViewState extends CoreGroupOptions { export interface GroupPanelViewState extends CoreGroupOptions {
@ -78,7 +77,6 @@ export interface GroupviewDropEvent {
export interface IHeader { export interface IHeader {
hidden: boolean; hidden: boolean;
height: number | undefined;
} }
export interface IDockviewGroupPanelModel extends IPanel { export interface IDockviewGroupPanelModel extends IPanel {

View File

@ -78,7 +78,7 @@ export class DockviewPanel
this.setTitle(params.title); this.setTitle(params.title);
this.view?.init({ this.view.init({
...params, ...params,
api: this.api, api: this.api,
containerApi: this.containerApi, 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 // the obtain the correct dimensions of the content panel we must deduct the tab height
this.api._onDidDimensionChange.fire({ this.api._onDidDimensionChange.fire({
width, width,
height: height - (this.group.model.header.height || 0), height: height,
}); });
this.view?.layout(width, height); this.view.layout(width, height);
} }
public dispose(): void { public dispose(): void {
this.api.dispose(); this.api.dispose();
this.view.dispose();
this.view?.dispose();
} }
} }

View File

@ -43,8 +43,7 @@ export class DockviewPanelModel implements IDockviewPanelModel {
readonly tabComponent?: string readonly tabComponent?: string
) { ) {
this._content = this.createContentComponent(this.id, contentComponent); this._content = this.createContentComponent(this.id, contentComponent);
this._tab = this._tab = this.createTabComponent(this.id, tabComponent);
this.createTabComponent(this.id, tabComponent) ?? new DefaultTab();
} }
init(params: GroupPanelPartInitParameters): void { init(params: GroupPanelPartInitParameters): void {
@ -108,13 +107,26 @@ export class DockviewPanelModel implements IDockviewPanelModel {
id: string, id: string,
componentName?: string componentName?: string
): ITabRenderer { ): ITabRenderer {
if (componentName) {
return createComponent( return createComponent(
id, id,
componentName, componentName,
this.accessor.options.tabComponents || {}, this.accessor.options.tabComponents,
this.accessor.options.frameworkTabComponents, this.accessor.options.frameworkTabComponents,
this.accessor.options.frameworkComponentFactory?.tab, this.accessor.options.frameworkComponentFactory?.tab,
() => new DefaultTab() () => 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();
}
} }
} }

View File

@ -75,7 +75,6 @@ export interface DockviewComponentOptions extends DockviewRenderFunctions {
watermarkComponent?: WatermarkConstructor; watermarkComponent?: WatermarkConstructor;
watermarkFrameworkComponent?: any; watermarkFrameworkComponent?: any;
frameworkComponentFactory?: GroupPanelFrameworkComponentFactory; frameworkComponentFactory?: GroupPanelFrameworkComponentFactory;
tabHeight?: number;
orientation?: Orientation; orientation?: Orientation;
styles?: ISplitviewStyles; styles?: ISplitviewStyles;
defaultTabComponent?: string; defaultTabComponent?: string;

View File

@ -464,7 +464,22 @@ export class Gridview implements IDisposable {
const childReference = oldRoot.children[0]; const childReference = oldRoot.children[0];
oldRoot.removeChild(0); // remove to prevent disposal when disposing of unwanted root oldRoot.removeChild(0); // remove to prevent disposal when disposing of unwanted root
oldRoot.dispose(); 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 { } else {
this._root.addChild(oldRoot, Sizing.Distribute, 0); this._root.addChild(oldRoot, Sizing.Distribute, 0);
} }

View File

@ -14,7 +14,6 @@ export interface GridviewComponentOptions {
[componentName: string]: any; [componentName: string]: any;
}; };
frameworkComponentFactory?: FrameworkFactory<GridviewPanel>; frameworkComponentFactory?: FrameworkFactory<GridviewPanel>;
tabHeight?: number;
styles?: ISplitviewStyles; styles?: ISplitviewStyles;
parentElement?: HTMLElement; parentElement?: HTMLElement;
} }

View File

@ -102,6 +102,10 @@
position: absolute; position: absolute;
z-index: 99; z-index: 99;
outline: none; 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 { &:active {
transition: background-color 0.1s ease-in-out; transition: background-color 0.1s ease-in-out;

View File

@ -1,6 +1,9 @@
{ {
"extends": "../../module-build/tsconfig.esm.json", "extends": "../../tsconfig.base.json",
"compilerOptions": { "compilerOptions": {
"module": "ES2020",
"moduleResolution": "node",
"target": "es6",
"outDir": "dist/esm", "outDir": "dist/esm",
"tsBuildInfoFile": ".build/tsconfig.tsbuildinfo.esm", "tsBuildInfoFile": ".build/tsconfig.tsbuildinfo.esm",
"jsx": "react", "jsx": "react",

View File

@ -1,5 +1,5 @@
{ {
"extends": "../../module-build/tsconfig.json", "extends": "../../tsconfig.base.json",
"compilerOptions": { "compilerOptions": {
"outDir": "dist/cjs", "outDir": "dist/cjs",
"tsBuildInfoFile": ".build/tsconfig.tsbuildinfo.cjs", "tsBuildInfoFile": ".build/tsconfig.tsbuildinfo.cjs",

View File

@ -1,7 +1,4 @@
{ {
"out": "typedocs", "extends": ["../../typedoc.base.json"],
"entryPoints": ["./src/index.ts"], "entryPoints": ["src/index.ts"]
"exclude": ["**/_test/**/*.*", "**/index.ts"],
"excludeExternals": true,
"excludePrivate": true
} }

View File

@ -17,8 +17,6 @@
Please see the website: https://dockview.dev Please see the website: https://dockview.dev
Want to inspect the latest deployment? Go to https://unpkg.com/browse/dockview@latest/
## Features ## Features
- Simple splitviews, nested splitviews (i.e. gridviews) supporting full layout managment with - 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 - Tabular docking and Drag and Drop support
- Documentation and examples - 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 ## 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 npm install --save dockview

View File

@ -27,7 +27,7 @@ const config: JestConfigWithTsJest = {
'^.+\\.tsx?$': [ '^.+\\.tsx?$': [
'ts-jest', 'ts-jest',
{ {
tsconfig, tsconfig: '<rootDir>/tsconfig.test.json',
}, },
], ],
}, },

View File

@ -1,6 +1,6 @@
{ {
"name": "dockview", "name": "dockview",
"version": "1.7.0", "version": "1.7.2",
"description": "Zero dependency layout manager supporting tabs, grids and splitviews with ReactJS support", "description": "Zero dependency layout manager supporting tabs, grids and splitviews with ReactJS support",
"main": "./dist/cjs/index.js", "main": "./dist/cjs/index.js",
"types": "./dist/cjs/index.d.ts", "types": "./dist/cjs/index.d.ts",
@ -20,7 +20,7 @@
"build:esm": "cross-env ../../node_modules/.bin/tsc --project ./tsconfig.esm.json --extendedDiagnostics", "build:esm": "cross-env ../../node_modules/.bin/tsc --project ./tsconfig.esm.json --extendedDiagnostics",
"build:modulefiles": "rollup -c", "build:modulefiles": "rollup -c",
"build": "npm run build:ci && npm run build:modulefiles", "build": "npm run build:ci && npm run build:modulefiles",
"clean": "rimraf dist/ .build/", "clean": "rimraf dist/ .build/ .rollup.cache/",
"docs": "typedoc", "docs": "typedoc",
"prepublishOnly": "npm run rebuild && npm run test", "prepublishOnly": "npm run rebuild && npm run test",
"rebuild": "npm run clean && npm run build", "rebuild": "npm run clean && npm run build",
@ -56,7 +56,7 @@
"author": "https://github.com/mathuo", "author": "https://github.com/mathuo",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"dockview-core": "^1.7.0" "dockview-core": "^1.7.2"
}, },
"devDependencies": { "devDependencies": {
"@rollup/plugin-node-resolve": "^15.0.1", "@rollup/plugin-node-resolve": "^15.0.1",
@ -71,7 +71,6 @@
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"rimraf": "^4.1.2", "rimraf": "^4.1.2",
"rollup": "^3.15.0", "rollup": "^3.15.0",
"rollup-plugin-postcss": "^4.0.2", "rollup-plugin-postcss": "^4.0.2"
"typedoc": "^0.23.25"
} }
} }

View File

@ -64,6 +64,9 @@ function createBundle(format, options) {
}), }),
typescript({ typescript({
tsconfig: 'tsconfig.esm.json', tsconfig: 'tsconfig.esm.json',
compilerOptions: {
declaration: false,
},
}), }),
]; ];

View File

@ -1,5 +1,3 @@
import * as React from 'react';
export function setMockRefElement(node: Partial<HTMLElement>): void { export function setMockRefElement(node: Partial<HTMLElement>): void {
const mockRef = { const mockRef = {
get current() { get current() {
@ -10,5 +8,5 @@ export function setMockRefElement(node: Partial<HTMLElement>): void {
}, },
}; };
jest.spyOn(React, 'useRef').mockReturnValueOnce(mockRef); jest.spyOn(require('react'), 'useRef').mockReturnValueOnce(mockRef);
} }

View File

@ -48,7 +48,7 @@ describe('gridview react', () => {
render(<DockviewReact components={components} onReady={onReady} />); render(<DockviewReact components={components} onReady={onReady} />);
expect(api.width).toBe(650); expect(api!.width).toBe(650);
expect(api.height).toBe(450); expect(api!.height).toBe(450);
}); });
}); });

View File

@ -59,7 +59,7 @@ describe('gridview react', () => {
/> />
); );
expect(api.width).toBe(650); expect(api!.width).toBe(650);
expect(api.height).toBe(450); expect(api!.height).toBe(450);
}); });
}); });

View File

@ -46,7 +46,7 @@ describe('gridview react', () => {
render(<PaneviewReact components={components} onReady={onReady} />); render(<PaneviewReact components={components} onReady={onReady} />);
expect(api.width).toBe(650); expect(api!.width).toBe(650);
expect(api.height).toBe(450); expect(api!.height).toBe(450);
}); });
}); });

View File

@ -18,7 +18,7 @@ describe('react', () => {
render(<TestWrapper onReady={onReady} component={Component} />); render(<TestWrapper onReady={onReady} component={Component} />);
expect(api).toBeTruthy(); expect(api!).toBeTruthy();
expect(screen.getByTestId('valueA').textContent).toBe('stringA'); expect(screen.getByTestId('valueA').textContent).toBe('stringA');
expect(screen.getByTestId('valueB').textContent).toBe('42'); expect(screen.getByTestId('valueB').textContent).toBe('42');
@ -60,7 +60,7 @@ const TestWrapper = (props: {
React.useEffect(() => { React.useEffect(() => {
const cut = new ReactPart<TestInterface>( const cut = new ReactPart<TestInterface>(
ref.current, ref.current!,
{ {
addPortal: (portal: React.ReactPortal) => { addPortal: (portal: React.ReactPortal) => {
setPortal((_) => [..._, portal]); setPortal((_) => [..._, portal]);

View File

@ -47,7 +47,7 @@ describe('dockview', () => {
render(<DockviewReact components={components} onReady={onReady} />); render(<DockviewReact components={components} onReady={onReady} />);
expect(api.width).toBe(650); expect(api!.width).toBe(650);
expect(api.height).toBe(450); expect(api!.height).toBe(450);
}); });
}); });

View File

@ -58,7 +58,7 @@ describe('gridview react', () => {
/> />
); );
expect(api.width).toBe(650); expect(api!.width).toBe(650);
expect(api.height).toBe(450); expect(api!.height).toBe(450);
}); });
}); });

View File

@ -46,7 +46,7 @@ describe('gridview react', () => {
render(<PaneviewReact components={components} onReady={onReady} />); render(<PaneviewReact components={components} onReady={onReady} />);
expect(api.width).toBe(650); expect(api!.width).toBe(650);
expect(api.height).toBe(450); expect(api!.height).toBe(450);
}); });
}); });

View File

@ -18,7 +18,7 @@ describe('react', () => {
render(<TestWrapper onReady={onReady} component={Component} />); render(<TestWrapper onReady={onReady} component={Component} />);
expect(api).toBeTruthy(); expect(api!).toBeTruthy();
expect(screen.getByTestId('valueA').textContent).toBe('stringA'); expect(screen.getByTestId('valueA').textContent).toBe('stringA');
expect(screen.getByTestId('valueB').textContent).toBe('42'); expect(screen.getByTestId('valueB').textContent).toBe('42');
@ -60,7 +60,7 @@ const TestWrapper = (props: {
React.useEffect(() => { React.useEffect(() => {
const cut = new ReactPart<TestInterface>( const cut = new ReactPart<TestInterface>(
ref.current, ref.current!,
{ {
addPortal: (portal: React.ReactPortal) => { addPortal: (portal: React.ReactPortal) => {
setPortal((_) => [..._, portal]); setPortal((_) => [..._, portal]);

View File

@ -58,7 +58,7 @@ describe('splitview react', () => {
/> />
); );
expect(api.width).toBe(650); expect(api!.width).toBe(650);
expect(api.height).toBe(450); expect(api!.height).toBe(450);
}); });
}); });

View File

@ -58,7 +58,7 @@ describe('splitview react', () => {
/> />
); );
expect(api.width).toBe(650); expect(api!.width).toBe(650);
expect(api.height).toBe(450); expect(api!.height).toBe(450);
}); });
}); });

View File

@ -37,7 +37,7 @@ function createGroupControlElement(
: undefined; : undefined;
} }
export interface IGroupPanelBaseProps<T extends {} = Record<string, any>> export interface IGroupPanelBaseProps<T extends { [index: string]: any } = any>
extends PanelParameters<T> { extends PanelParameters<T> {
api: DockviewPanelApi; api: DockviewPanelApi;
containerApi: DockviewApi; containerApi: DockviewApi;
@ -59,7 +59,6 @@ export interface IDockviewReactProps {
components: PanelCollection<IDockviewPanelProps>; components: PanelCollection<IDockviewPanelProps>;
tabComponents?: PanelCollection<IDockviewPanelHeaderProps>; tabComponents?: PanelCollection<IDockviewPanelHeaderProps>;
watermarkComponent?: React.FunctionComponent<IWatermarkPanelProps>; watermarkComponent?: React.FunctionComponent<IWatermarkPanelProps>;
tabHeight?: number;
onDidDrop?: (event: DockviewDropEvent) => void; onDidDrop?: (event: DockviewDropEvent) => void;
showDndOverlay?: (event: DockviewDndOverlayEvent) => boolean; showDndOverlay?: (event: DockviewDndOverlayEvent) => boolean;
hideBorders?: boolean; hideBorders?: boolean;
@ -143,7 +142,6 @@ export const DockviewReact = React.forwardRef(
frameworkComponentFactory: factory, frameworkComponentFactory: factory,
frameworkComponents: props.components, frameworkComponents: props.components,
frameworkTabComponents, frameworkTabComponents,
tabHeight: props.tabHeight,
watermarkFrameworkComponent: props.watermarkComponent, watermarkFrameworkComponent: props.watermarkComponent,
defaultTabComponent: props.defaultTabComponent defaultTabComponent: props.defaultTabComponent
? DEFAULT_REACT_TAB ? DEFAULT_REACT_TAB

View File

@ -1,6 +1,9 @@
{ {
"extends": "../../module-build/tsconfig.esm.json", "extends": "../../tsconfig.base.json",
"compilerOptions": { "compilerOptions": {
"module": "ES2020",
"moduleResolution": "node",
"target": "es6",
"outDir": "dist/esm", "outDir": "dist/esm",
"tsBuildInfoFile": ".build/tsconfig.tsbuildinfo.esm", "tsBuildInfoFile": ".build/tsconfig.tsbuildinfo.esm",
"jsx": "react", "jsx": "react",

View File

@ -1,14 +1,11 @@
{ {
"extends": "../../module-build/tsconfig.json", "extends": "../../tsconfig.base.json",
"compilerOptions": { "compilerOptions": {
"outDir": "dist/cjs", "outDir": "dist/cjs",
"tsBuildInfoFile": ".build/tsconfig.tsbuildinfo.cjs", "tsBuildInfoFile": ".build/tsconfig.tsbuildinfo.cjs",
"jsx": "react", "jsx": "react",
"rootDir": "src" "rootDir": "src"
}, },
"paths": {
"dockview-core": "../dockview-core"
},
"include": ["src"], "include": ["src"],
"exclude": ["**/node_modules", "src/__tests__"] "exclude": ["**/node_modules", "src/__tests__"]
} }

View File

@ -1,7 +1,5 @@
{ {
"out": "typedocs", "extends": ["../../typedoc.base.json"],
"entryPoints": ["./src/index.ts"], "entryPoints": ["src/index.ts"],
"exclude": ["**/_test/**/*.*", "**/index.ts"], "exclude": ["**/dist/**"]
"excludeExternals": true,
"excludePrivate": true
} }

View File

@ -0,0 +1,27 @@
---
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)
- 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
- 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
- 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)

View File

@ -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

View File

@ -8,7 +8,6 @@ import { SimpleSplitview2 } from '@site/src/components/simpleSplitview2';
# Basics # Basics
asd
This section will take you through a number of concepts that can be applied to all dockview components. This section will take you through a number of concepts that can be applied to all dockview components.
## Panels ## Panels
@ -53,19 +52,16 @@ const MyComponent = (props: ISplitviewPanelProps<{ title: string }>) => {
const disposable = props.api.onDidActiveChange((event) => { const disposable = props.api.onDidActiveChange((event) => {
console.log(`is panel active: ${event.isActive}`); console.log(`is panel active: ${event.isActive}`);
}); });
return () => { return () => {
disposable.dispose(); // remember to dispose of any subscriptions disposable.dispose(); // remember to dispose of any subscriptions
}; };
}, [props.api]); }, [props.api]);
const addAnotherPanel = React.useCallback(() => { const addAnotherPanel = React.useCallback(() => {
props.containerApi.addPanel({ props.containerApi.addPanel({
id: 'another_id', id: 'another_id',
component: 'anotherComponent', component: 'anotherComponent',
}); });
}, [props.containerApi]); }, [props.containerApi]);
return ( return (
<div> <div>
<span>{`My first panel has the title: ${props.params.title}`}</span> <span>{`My first panel has the title: ${props.params.title}`}</span>
@ -98,7 +94,6 @@ React.useEffect(() => {
const disposable = api.onDidFocusChange(() => { const disposable = api.onDidFocusChange(() => {
// write some code // write some code
}); });
return () => { return () => {
disposable.dispose(); disposable.dispose();
}; };

View File

@ -2,7 +2,10 @@
description: Dockview Documentation 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 Link from '@docusaurus/Link';
import useBaseUrl from '@docusaurus/useBaseUrl'; import useBaseUrl from '@docusaurus/useBaseUrl';
@ -23,7 +26,12 @@ import DockviewSetTitle from '@site/sandboxes/updatetitle-dockview/src/app';
import RenderingDockview from '@site/sandboxes/rendering-dockview/src/app'; import RenderingDockview from '@site/sandboxes/rendering-dockview/src/app';
import DockviewExternalDnd from '@site/sandboxes/externaldnd-dockview/src/app'; import DockviewExternalDnd from '@site/sandboxes/externaldnd-dockview/src/app';
import DockviewResizeContainer from '@site/sandboxes/resizecontainer-dockview/src/app'; import DockviewResizeContainer from '@site/sandboxes/resizecontainer-dockview/src/app';
import { attach as attachDockviewVanilla } from '@site/sandboxes/vanilla-dockview/src/app'; 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 # Dockview
@ -31,12 +39,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. Dockview is an abstraction built on top of [Gridviews](./gridview) where each view is a container of many tabbed panels.
<Container sandboxId="simple-dockview"> <MultiFrameworkContainer
<SimpleDockview /> sandboxId="simple-dockview"
</Container> react={SimpleDockview}
typescript={attachSimpleDockview}
/>
You can access the panels associated group through the `panel.group` variable. <br />
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 ## DockviewReact Component
@ -59,7 +71,6 @@ import { DockviewReact } from 'dockview';
| showDndOverlay | Event | Yes | false | | | showDndOverlay | Event | Yes | false | |
| defaultTabComponent | object | Yes | | | | defaultTabComponent | object | Yes | | |
| groupControlComponent | object | Yes | | | | groupControlComponent | object | Yes | | |
| tabHeight | number | Yes | | |
| singleTabMode | 'fullwidth' \| 'default' | Yes | 'default' | | | singleTabMode | 'fullwidth' \| 'default' | Yes | 'default' | |
## Dockview API ## Dockview API
@ -113,8 +124,6 @@ const onReady = (event: DockviewReadyEvent) => {
| removeGroup | `(group: GroupPanel): void` | | | removeGroup | `(group: GroupPanel): void` | |
| getGroup | `(id: string): GroupPanel \| undefined` | | | getGroup | `(id: string): GroupPanel \| undefined` | |
| | | | | | | |
| getTabHeight | `(): number \| undefined` | |
| setTabHeight | `(height: number \| undefined): void` | |
| updateOptions | `(options:SplitviewComponentUpdateOptions): void` | | | updateOptions | `(options:SplitviewComponentUpdateOptions): void` | |
| focus | `(): void` | | | focus | `(): void` | |
| layout | `(width: number, height:number): void` | <Link to="../basics/#auto-resizing">Auto Resizing</Link> | | layout | `(width: number, height:number): void` | <Link to="../basics/#auto-resizing">Auto Resizing</Link> |
@ -342,7 +351,9 @@ return (
### Third Party Dnd Libraries ### 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.
<Container> <Container>
<DockviewExternalDnd /> <DockviewExternalDnd />
@ -608,9 +619,21 @@ to the entire width of the group. For example:
<DockviewReactComponent singleTabMode="fullwidth" {...otherProps} /> <DockviewReactComponent singleTabMode="fullwidth" {...otherProps} />
``` ```
<Container sandboxId="fullwidthtab-dockview"> <MultiFrameworkContainer
<DockviewNative /> sandboxId="fullwidthtab-dockview"
</Container> react={DockviewNative}
typescript={attachNativeDockview}
/>
### Tab Height
Tab height can be controlled through CSS.
<MultiFrameworkContainer
sandboxId="tabheight-dockview"
react={DockviewTabheight}
typescript={attachTabHeightDockview}
/>
## Groups ## Groups
@ -701,18 +724,10 @@ If you wish to interact with the drop event from one dockview instance in anothe
<NestedDockview /> <NestedDockview />
</Container> </Container>
### Example ### Window-like mananger with tabs
hello
<DockviewNative2 /> <DockviewNative2 />
hello 2
<div style={{ height: '400px', width: '100%' }}>
<App />
</div>
## Vanilla JS ## Vanilla JS
> Note: This section is experimental and support for Vanilla JS is a work in progress. > Note: This section is experimental and support for Vanilla JS is a work in progress.
@ -723,4 +738,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`. > 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`. > `dockview-core` is a dependency of `dockview` and automatically installed during the installation process of `dockview` via `npm install dockview`.
<Container injectVanillaJS={attachDockviewVanilla} /> <Container
sandboxId="typescript/vanilla-dockview"
injectVanillaJS={attachDockviewVanilla}
/>

View File

@ -1,6 +1,6 @@
{ {
"name": "dockview-docs", "name": "dockview-docs",
"version": "1.7.0", "version": "1.7.2",
"private": true, "private": true,
"scripts": { "scripts": {
"docusaurus": "docusaurus", "docusaurus": "docusaurus",
@ -12,8 +12,7 @@
"serve": "docusaurus serve", "serve": "docusaurus serve",
"write-translations": "docusaurus write-translations", "write-translations": "docusaurus write-translations",
"write-heading-ids": "docusaurus write-heading-ids", "write-heading-ids": "docusaurus write-heading-ids",
"typecheck": "tsc", "typecheck": "tsc"
"deploy-docs": "node scripts/package-docs.js"
}, },
"dependencies": { "dependencies": {
"@docusaurus/core": "^2.4.0", "@docusaurus/core": "^2.4.0",
@ -23,7 +22,7 @@
"@minoru/react-dnd-treeview": "^3.4.3", "@minoru/react-dnd-treeview": "^3.4.3",
"axios": "^1.3.3", "axios": "^1.3.3",
"clsx": "^1.2.1", "clsx": "^1.2.1",
"dockview": "^1.7.0", "dockview": "^1.7.2",
"prism-react-renderer": "^1.3.5", "prism-react-renderer": "^1.3.5",
"react": "^18.2.0", "react": "^18.2.0",
"react-dnd": "^16.0.1", "react-dnd": "^16.0.1",

View File

@ -14,7 +14,6 @@
"noImplicitThis": true, "noImplicitThis": true,
"noImplicitAny": true, "noImplicitAny": true,
"strictNullChecks": true, "strictNullChecks": true,
"suppressImplicitAnyIndexErrors": true,
"noUnusedLocals": true "noUnusedLocals": true
} }
} }

View File

@ -7,25 +7,25 @@ import {
} from 'dockview'; } from 'dockview';
import * as React from 'react'; import * as React from 'react';
interface CustomProps {
valueA: string;
}
const components = { const components = {
default: (props: IDockviewPanelProps<{ title: string }>) => { default: (props: IDockviewPanelProps<CustomProps>) => {
return <div style={{ padding: '20px' }}>{props.params.title}</div>; return <div style={{ padding: '20px' }}>{props.api.title}</div>;
}, },
}; };
const MyCustomheader = (props: IDockviewPanelHeaderProps) => {
const onContextMenu = (event: React.MouseEvent) => {
event.preventDefault();
alert('context menu');
};
return <DockviewDefaultTab onContextMenu={onContextMenu} {...props} />;
};
const headerComponents = { const headerComponents = {
default: (props: IDockviewPanelHeaderProps) => { default: (props: IDockviewPanelHeaderProps<CustomProps>) => {
const onContextMenu = (event: React.MouseEvent) => { const onContextMenu = (event: React.MouseEvent) => {
event.preventDefault(); event.preventDefault();
alert('context menu'); alert(
`This custom header was parsed the params ${JSON.stringify(
props.params
)}`
);
}; };
return <DockviewDefaultTab onContextMenu={onContextMenu} {...props} />; return <DockviewDefaultTab onContextMenu={onContextMenu} {...props} />;
}, },
@ -39,34 +39,52 @@ const CustomHeadersDockview = () => {
id: 'panel_1', id: 'panel_1',
component: 'default', component: 'default',
title: 'Panel 1', title: 'Panel 1',
params: {
valueA: 'test value',
},
}); });
event.api.addPanel({ event.api.addPanel({
id: 'panel_2', id: 'panel_2',
component: 'default', component: 'default',
title: 'Panel 2', title: 'Panel 2',
params: {
valueA: 'test value',
},
}); });
event.api.addPanel({ event.api.addPanel({
id: 'panel_3', id: 'panel_3',
component: 'default', component: 'default',
title: 'Panel 3', title: 'Panel 3',
params: {
valueA: 'test value',
},
}); });
event.api.addPanel({ event.api.addPanel({
id: 'panel_4', id: 'panel_4',
component: 'default', component: 'default',
title: 'Panel 4', title: 'Panel 4',
position: { referencePanel: 'panel_3', direction: 'right' }, position: { referencePanel: 'panel_3', direction: 'right' },
params: {
valueA: 'test value',
},
}); });
event.api.addPanel({ event.api.addPanel({
id: 'panel_5', id: 'panel_5',
component: 'default', component: 'default',
title: 'Panel 5', title: 'Panel 5',
position: { referencePanel: 'panel_4', direction: 'within' }, position: { referencePanel: 'panel_4', direction: 'within' },
params: {
valueA: 'test value',
},
}); });
const panel6 = event.api.addPanel({ const panel6 = event.api.addPanel({
id: 'panel_6', id: 'panel_6',
component: 'default', component: 'default',
title: 'Panel 6', title: 'Panel 6',
position: { referencePanel: 'panel_4', direction: 'below' }, position: { referencePanel: 'panel_4', direction: 'below' },
params: {
valueA: 'test value',
},
}); });
panel6.group.locked = true; panel6.group.locked = true;
panel6.group.header.hidden = true; panel6.group.header.hidden = true;
@ -75,6 +93,9 @@ const CustomHeadersDockview = () => {
component: 'default', component: 'default',
title: 'Panel 7', title: 'Panel 7',
position: { referencePanel: 'panel_6', direction: 'right' }, position: { referencePanel: 'panel_6', direction: 'right' },
params: {
valueA: 'test value',
},
}); });
event.api.addPanel({ event.api.addPanel({
id: 'panel_8', id: 'panel_8',
@ -82,6 +103,9 @@ const CustomHeadersDockview = () => {
title: 'Panel 8', title: 'Panel 8',
position: { referencePanel: 'panel_7', direction: 'within' }, position: { referencePanel: 'panel_7', direction: 'within' },
params: {
valueA: 'test value',
},
}); });
event.api.addGroup(); event.api.addGroup();

View File

@ -14,7 +14,6 @@
"noImplicitThis": true, "noImplicitThis": true,
"noImplicitAny": true, "noImplicitAny": true,
"strictNullChecks": true, "strictNullChecks": true,
"suppressImplicitAnyIndexErrors": true,
"noUnusedLocals": true "noUnusedLocals": true
} }
} }

View File

@ -14,7 +14,6 @@
"noImplicitThis": true, "noImplicitThis": true,
"noImplicitAny": true, "noImplicitAny": true,
"strictNullChecks": true, "strictNullChecks": true,
"suppressImplicitAnyIndexErrors": true,
"noUnusedLocals": true "noUnusedLocals": true
} }
} }

View File

@ -18,6 +18,29 @@ const components = {
}, },
}; };
const DraggableElement = () => (
<span
tabIndex={-1}
onDragStart={(event) => {
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
</span>
);
const DndDockview = (props: { renderVisibleOnly: boolean }) => { const DndDockview = (props: { renderVisibleOnly: boolean }) => {
const onReady = (event: DockviewReadyEvent) => { const onReady = (event: DockviewReadyEvent) => {
event.api.addPanel({ event.api.addPanel({
@ -77,19 +100,9 @@ const DndDockview = (props: { renderVisibleOnly: boolean }) => {
height: '100%', height: '100%',
}} }}
> >
<div <div style={{ margin: '2px 0px' }}>
style={{ <DraggableElement />
backgroundColor: 'orange',
padding: '0px 8px',
borderRadius: '4px',
width: '100px',
cursor: 'pointer',
}}
draggable={true}
>
Drag me
</div> </div>
<DockviewReact <DockviewReact
components={components} components={components}
onReady={onReady} onReady={onReady}

View File

@ -14,7 +14,6 @@
"noImplicitThis": true, "noImplicitThis": true,
"noImplicitAny": true, "noImplicitAny": true,
"strictNullChecks": true, "strictNullChecks": true,
"suppressImplicitAnyIndexErrors": true,
"noUnusedLocals": true "noUnusedLocals": true
} }
} }

View File

@ -14,7 +14,6 @@
"noImplicitThis": true, "noImplicitThis": true,
"noImplicitAny": true, "noImplicitAny": true,
"strictNullChecks": true, "strictNullChecks": true,
"suppressImplicitAnyIndexErrors": true,
"noUnusedLocals": true "noUnusedLocals": true
} }
} }

View File

@ -14,7 +14,6 @@
"noImplicitThis": true, "noImplicitThis": true,
"noImplicitAny": true, "noImplicitAny": true,
"strictNullChecks": true, "strictNullChecks": true,
"suppressImplicitAnyIndexErrors": true,
"noUnusedLocals": true "noUnusedLocals": true
} }
} }

View File

@ -50,7 +50,6 @@ const SampleData = [
const TreeComponent = () => { const TreeComponent = () => {
const [treeData, setTreeData] = useState(SampleData); const [treeData, setTreeData] = useState(SampleData);
const handleDrop = (newTreeData: any) => { const handleDrop = (newTreeData: any) => {
console.log('handleDrop');
setTreeData(newTreeData); setTreeData(newTreeData);
}; };
return ( return (

View File

@ -14,7 +14,6 @@
"noImplicitThis": true, "noImplicitThis": true,
"noImplicitAny": true, "noImplicitAny": true,
"strictNullChecks": true, "strictNullChecks": true,
"suppressImplicitAnyIndexErrors": true,
"noUnusedLocals": true "noUnusedLocals": true
} }
} }

View File

@ -14,7 +14,6 @@
"noImplicitThis": true, "noImplicitThis": true,
"noImplicitAny": true, "noImplicitAny": true,
"strictNullChecks": true, "strictNullChecks": true,
"suppressImplicitAnyIndexErrors": true,
"noUnusedLocals": true "noUnusedLocals": true
} }
} }

View File

@ -14,7 +14,6 @@
"noImplicitThis": true, "noImplicitThis": true,
"noImplicitAny": true, "noImplicitAny": true,
"strictNullChecks": true, "strictNullChecks": true,
"suppressImplicitAnyIndexErrors": true,
"noUnusedLocals": true "noUnusedLocals": true
} }
} }

View File

@ -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"
]
}

View File

@ -0,0 +1,17 @@
.my-custom-tab {
padding: 0px 8px;
width: 100%;
display: flex;
height: 100%;
align-items: center;
background-color: var(--dv-tabs-and-actions-container-background-color);
.my-custom-tab-icon {
font-size: 16px;
&:hover {
border-radius: 2px;
background-color: var(--dv-icon-hover-background-color);
}
}
}

View File

@ -0,0 +1,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<Parameters>): void {
this.render(event.params);
}
private render(params: Record<string, any>) {
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<Parameters>): void {
this.render(event.params);
}
private render(params: Record<string, any>) {
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();
},
};
}

View File

@ -1,5 +1,5 @@
import './styles.css'; import './styles.css';
import 'dockview/dist/styles/dockview.css'; import 'dockview-core/dist/styles/dockview.css';
import { attach } from './app'; import { attach } from './app';

View File

@ -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
}
}

View File

@ -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"
]
}

View File

@ -0,0 +1,44 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="theme-color" content="#000000">
<!--
manifest.json provides metadata used when your web app is added to the
homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
</head>
<body>
<noscript>
You need to enable JavaScript to run this app.
</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>

View File

@ -0,0 +1,120 @@
import {
DockviewComponent,
IContentRenderer,
IGroupPanelInitParameters,
PanelUpdateEvent,
Parameters,
} 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;
}
update(event: PanelUpdateEvent<Parameters>): void {
this._element.textContent = event.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();
},
};
}

View File

@ -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);
}

View File

@ -0,0 +1,16 @@
body {
margin: 0px;
color: white;
font-family: sans-serif;
text-align: center;
}
#root {
height: 100vh;
width: 100vw;
}
.app {
height: 100%;
}

View File

@ -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
}
}

View File

@ -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"
]
}

View File

@ -0,0 +1,44 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="theme-color" content="#000000">
<!--
manifest.json provides metadata used when your web app is added to the
homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
</head>
<body>
<noscript>
You need to enable JavaScript to run this app.
</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>

View File

@ -0,0 +1,3 @@
.skinny-tabs {
--dv-tabs-and-actions-container-height: 20px;
}

View File

@ -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<Parameters>): 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();
},
};
}

View File

@ -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);
}

View File

@ -0,0 +1,16 @@
body {
margin: 0px;
color: white;
font-family: sans-serif;
text-align: center;
}
#root {
height: 100vh;
width: 100vw;
}
.app {
height: 100%;
}

View File

@ -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
}
}

View File

@ -0,0 +1,44 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="theme-color" content="#000000">
<!--
manifest.json provides metadata used when your web app is added to the
homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
</head>
<body>
<noscript>
You need to enable JavaScript to run this app.
</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>

View File

@ -2,7 +2,7 @@ import {
DockviewComponent, DockviewComponent,
IContentRenderer, IContentRenderer,
IGroupPanelInitParameters, IGroupPanelInitParameters,
} from 'dockview'; } from 'dockview-core';
class DefaultPanel implements IContentRenderer { class DefaultPanel implements IContentRenderer {
private _element: HTMLElement; private _element: HTMLElement;

View File

@ -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);
}

View File

@ -0,0 +1,16 @@
body {
margin: 0px;
color: white;
font-family: sans-serif;
text-align: center;
}
#root {
height: 100vh;
width: 100vw;
}
.app {
height: 100%;
}

View File

@ -14,7 +14,6 @@
"noImplicitThis": true, "noImplicitThis": true,
"noImplicitAny": true, "noImplicitAny": true,
"strictNullChecks": true, "strictNullChecks": true,
"suppressImplicitAnyIndexErrors": true,
"noUnusedLocals": true "noUnusedLocals": true
} }
} }

View File

@ -14,7 +14,6 @@
"noImplicitThis": true, "noImplicitThis": true,
"noImplicitAny": true, "noImplicitAny": true,
"strictNullChecks": true, "strictNullChecks": true,
"suppressImplicitAnyIndexErrors": true,
"noUnusedLocals": true "noUnusedLocals": true
} }
} }

View File

@ -14,7 +14,6 @@
"noImplicitThis": true, "noImplicitThis": true,
"noImplicitAny": true, "noImplicitAny": true,
"strictNullChecks": true, "strictNullChecks": true,
"suppressImplicitAnyIndexErrors": true,
"noUnusedLocals": true "noUnusedLocals": true
} }
} }

View File

@ -14,7 +14,6 @@
"noImplicitThis": true, "noImplicitThis": true,
"noImplicitAny": true, "noImplicitAny": true,
"strictNullChecks": true, "strictNullChecks": true,
"suppressImplicitAnyIndexErrors": true,
"noUnusedLocals": true "noUnusedLocals": true
} }
} }

View File

@ -14,7 +14,6 @@
"noImplicitThis": true, "noImplicitThis": true,
"noImplicitAny": true, "noImplicitAny": true,
"strictNullChecks": true, "strictNullChecks": true,
"suppressImplicitAnyIndexErrors": true,
"noUnusedLocals": true "noUnusedLocals": true
} }
} }

View File

@ -14,7 +14,6 @@
"noImplicitThis": true, "noImplicitThis": true,
"noImplicitAny": true, "noImplicitAny": true,
"strictNullChecks": true, "strictNullChecks": true,
"suppressImplicitAnyIndexErrors": true,
"noUnusedLocals": true "noUnusedLocals": true
} }
} }

View File

@ -14,7 +14,6 @@
"noImplicitThis": true, "noImplicitThis": true,
"noImplicitAny": true, "noImplicitAny": true,
"strictNullChecks": true, "strictNullChecks": true,
"suppressImplicitAnyIndexErrors": true,
"noUnusedLocals": true "noUnusedLocals": true
} }
} }

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