diff --git a/packages/dockview-demo/.eslintrc b/packages/dockview-demo/.eslintrc deleted file mode 100644 index 2e0ee58ad..000000000 --- a/packages/dockview-demo/.eslintrc +++ /dev/null @@ -1,8 +0,0 @@ -{ - "extends": [ - "../../.eslintrc" - ], - "rules": { - "@typescript-eslint/no-var-requires": "off" - } - } \ No newline at end of file diff --git a/packages/dockview-demo/.storybook/main.js b/packages/dockview-demo/.storybook/main.js deleted file mode 100644 index 276052b59..000000000 --- a/packages/dockview-demo/.storybook/main.js +++ /dev/null @@ -1,23 +0,0 @@ -module.exports = { - stories: [ - "../src/**/*.stories.mdx", - "../src/**/*.stories.@(js|jsx|ts|tsx)" - ], - addons: [ - { - name: '@storybook/addon-docs', - options: { - sourceLoaderOptions: { - injectStoryParameters: false, - }, - }, - }, - "@storybook/addon-storysource", - "@storybook/addon-links", - "@storybook/addon-essentials", - "@storybook/addon-controls" - ], - core: { - builder: "webpack5" - } -} \ No newline at end of file diff --git a/packages/dockview-demo/.storybook/preview.js b/packages/dockview-demo/.storybook/preview.js deleted file mode 100644 index a2e384710..000000000 --- a/packages/dockview-demo/.storybook/preview.js +++ /dev/null @@ -1,5 +0,0 @@ -import '!style-loader!css-loader!sass-loader!../node_modules/dockview/dist/styles/dockview.css'; - -export const parameters = { - actions: { argTypesRegex: "^on[A-Z].*" }, -} \ No newline at end of file diff --git a/packages/dockview-demo/package.json b/packages/dockview-demo/package.json deleted file mode 100644 index 6f654ed90..000000000 --- a/packages/dockview-demo/package.json +++ /dev/null @@ -1,51 +0,0 @@ -{ - "name": "dockview-demo", - "private": true, - "version": "1.5.1", - "description": "Demo project for https://github.com/mathuo/dockview", - "scripts": { - "build": "npm run build-webpack", - "build-webpack": "cross-env ../../node_modules/.bin/webpack --config webpack.config.js", - "start": "cross-env ../../node_modules/.bin/webpack serve --config webpack.config.js", - "clean": "rimraf dist/ .build/", - "storybook": "start-storybook -p 6006", - "build-storybook": "build-storybook" - }, - "author": "https://github.com/mathuo", - "license": "MIT", - "dependencies": { - "@mdx-js/react": "^2.1.1", - "dockview": "^1.5.1", - "react": "^18.1.0", - "react-dom": "^18.1.0", - "recoil": "^0.7.2" - }, - "devDependencies": { - "@babel/core": "^7.17.10", - "@storybook/addon-actions": "^6.5.0-beta.0", - "@storybook/addon-controls": "^6.5.0-beta.0", - "@storybook/addon-docs": "^6.5.0-beta.0", - "@storybook/addon-essentials": "^6.5.0-beta.0", - "@storybook/addon-links": "^6.5.0-beta.0", - "@storybook/addon-storysource": "^6.5.0-beta.0", - "@storybook/builder-webpack5": "^6.5.0-beta.0", - "@storybook/manager-webpack5": "^6.5.0-beta.0", - "@storybook/react": "^6.5.0-beta.0", - "@types/react": "^18.0.9", - "@types/react-dom": "^18.0.3", - "babel-loader": "^8.2.5", - "cross-env": "^7.0.3", - "rimraf": "^3.0.2", - "source-map-loader": "^3.0.1" - }, - "resolutions": { - "webpack": "^5.0.0", - "css-loader": "^5.0.0", - "dotenv-webpack": "^6.0.0", - "html-webpack-plugin": "^5.0.0", - "style-loader": "^2.0.0", - "terser-webpack-plugin": "^5.0.0", - "webpack-dev-middleware": "^4.1.0", - "webpack-virtual-modules": "^0.4.2" - } -} diff --git a/packages/dockview-demo/public/index.html b/packages/dockview-demo/public/index.html deleted file mode 100644 index 212d545a7..000000000 --- a/packages/dockview-demo/public/index.html +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - -
-
- - - diff --git a/packages/dockview-demo/src/dom.ts b/packages/dockview-demo/src/dom.ts deleted file mode 100644 index f55b853a6..000000000 --- a/packages/dockview-demo/src/dom.ts +++ /dev/null @@ -1,13 +0,0 @@ -export const toggleClass = ( - element: HTMLElement, - className: string, - isToggled: boolean -) => { - const hasClass = element.classList.contains(className); - if (isToggled && !hasClass) { - element.classList.add(className); - } - if (!isToggled && hasClass) { - element.classList.remove(className); - } -}; diff --git a/packages/dockview-demo/src/events.ts b/packages/dockview-demo/src/events.ts deleted file mode 100644 index 3308e578d..000000000 --- a/packages/dockview-demo/src/events.ts +++ /dev/null @@ -1,122 +0,0 @@ -import { IDisposable } from './lifecycle'; - -export interface Event { - (listener: (e: T) => any): IDisposable; -} - -export interface EmitterOptions { - replay?: boolean; -} - -export namespace Event { - export const any = (...children: Event[]): Event => { - return (listener: (e: T) => void) => { - const disposables = children.map((child) => child(listener)); - - return { - dispose: () => { - disposables.forEach((d) => { - d.dispose(); - }); - }, - }; - }; - }; -} - -// dumb event emitter with better typings than nodes event emitter -// https://github.com/microsoft/vscode/blob/master/src/vs/base/common/event.ts -export class Emitter implements IDisposable { - private _event?: Event; - - private _last?: T; - private _listeners: Array<(e: T) => any> = []; - private _disposed = false; - - constructor(private readonly options?: EmitterOptions) {} - - get event() { - if (!this._event) { - this._event = (listener: (e: T) => void): IDisposable => { - if (this.options?.replay && this._last !== undefined) { - listener(this._last); - } - - this._listeners.push(listener); - - return { - dispose: () => { - const index = this._listeners.indexOf(listener); - if (index > -1) { - this._listeners.splice(index, 1); - } - }, - }; - }; - } - return this._event; - } - - public fire(e: T) { - this._last = e; - for (const listener of this._listeners) { - listener(e); - } - } - - public dispose() { - this._listeners = []; - this._disposed = true; - } -} - -export function addDisposableWindowListener( - element: Window, - type: K, - listener: (this: Window, ev: WindowEventMap[K]) => any, - options?: boolean | AddEventListenerOptions -): IDisposable { - element.addEventListener(type, listener, options); - - return { - dispose: () => { - element.removeEventListener(type, listener); - }, - }; -} - -export function addDisposableListener( - element: HTMLElement, - type: K, - listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, - options?: boolean | AddEventListenerOptions -): IDisposable { - element.addEventListener(type, listener, options); - - return { - dispose: () => { - element.removeEventListener(type, listener); - }, - }; -} - -export class TickDelayedEvent implements IDisposable { - private timer: any; - - private readonly _onFired = new Emitter(); - readonly onEvent = this._onFired.event; - - fire(): void { - if (this.timer) { - clearTimeout(this.timer); - } - this.timer = setTimeout(() => { - this._onFired.fire(); - clearTimeout(this.timer); - }); - } - - dispose(): void { - this._onFired.dispose(); - } -} diff --git a/packages/dockview-demo/src/index.scss b/packages/dockview-demo/src/index.scss deleted file mode 100644 index df02ae80a..000000000 --- a/packages/dockview-demo/src/index.scss +++ /dev/null @@ -1,66 +0,0 @@ -@import '~dockview/dist/styles/dockview.css'; - -body { - margin: 0; - font-family: 'Roboto', sans-serif; - overflow: hidden; - color: rgb(204, 204, 204); - font-size: 13px; -} - -::-webkit-scrollbar { - height: 8px; - width: 8px; -} - -/* Track */ -::-webkit-scrollbar-track { - background: transparent; -} - -/* Handle */ -::-webkit-scrollbar-thumb { - background: var(--dv-tabs-container-scrollbar-color); -} - -// *, -// *::after, -// *::before { -// -webkit-user-drag: none; -// -webkit-app-region: no-drag; -// -webkit-user-select: none; -// } - -.close-action { - &:active { - -webkit-mask-size: 100% 100% !important; - mask-size: 100% 100% !important; - } - - background-color: white; - height: 16px; - width: 16px; - display: block; - -webkit-mask: var(--dv-tab-close-icon) 50% 50% / 90% 90% no-repeat; - mask: var(--dv-tab-close-icon) 50% 50% / 90% 90% no-repeat; - margin-right: '0.5em'; - cursor: pointer; -} - -button { - text-overflow: ellipsis; - overflow: hidden; - white-space: nowrap; - border: none; - background-color: rgb(14, 99, 156); - color: white; - font-family: inherit; - outline: none; - padding: 2px 14px; - margin: 2px 0px; - - &:hover { - background-color: rgb(17, 119, 187); - cursor: pointer; - } -} diff --git a/packages/dockview-demo/src/index.tsx b/packages/dockview-demo/src/index.tsx deleted file mode 100644 index 344dde775..000000000 --- a/packages/dockview-demo/src/index.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import * as React from 'react'; -import * as ReactDOM from 'react-dom/client'; -import { Application } from './layout-grid/application'; -import { RecoilRoot } from 'recoil'; -import './index.scss'; - -document.getElementById('app').classList.add('dockview-theme-dark'); - -const root = ReactDOM.createRoot(document.getElementById('app')); - -root.render( - - - -); diff --git a/packages/dockview-demo/src/layout-grid/activitybar.scss b/packages/dockview-demo/src/layout-grid/activitybar.scss deleted file mode 100644 index e6c8f20f3..000000000 --- a/packages/dockview-demo/src/layout-grid/activitybar.scss +++ /dev/null @@ -1,34 +0,0 @@ -.activity-bar { - height: 100%; - background-color: rgb(51, 51, 51); - - .activity-bar-item { - width: 100%; - padding-bottom: 100%; - display: flex; - justify-content: center; - align-items: center; - cursor: pointer; - position: relative; - - &:hover { - background-color: rgba(90, 90, 90, 0.5); - } - - .activity-bar-item-image { - -webkit-mask-size: 65% 65%; - mask-size: 65% 65%; - -webkit-mask-position: 50% 50%; - mask-position: 50% 50%; - -webkit-mask-repeat: no-repeat; - mask-repeat: no-repeat; - height: 100%; - width: 100%; - display: block; - background-color: gray; - position: absolute; - top: 0; - left: 0; - } - } -} diff --git a/packages/dockview-demo/src/layout-grid/activitybar.tsx b/packages/dockview-demo/src/layout-grid/activitybar.tsx deleted file mode 100644 index ba68ef956..000000000 --- a/packages/dockview-demo/src/layout-grid/activitybar.tsx +++ /dev/null @@ -1,71 +0,0 @@ -import * as React from 'react'; -import { GridviewApi, IGridviewPanelProps } from 'dockview'; -import './activitybar.scss'; -import { useLayoutRegistry } from './registry'; -import { CompositeDisposable } from '../lifecycle'; - -const ActivitybarImage = (props: { url: string }) => ( - -); - -export const Activitybar = (props: IGridviewPanelProps) => { - const registry = useLayoutRegistry(); - const [isActive, setActive] = React.useState(); - - const onOpenSidebar = (event: React.MouseEvent) => { - const api = registry.get('gridview'); - - const sidebarPanel = api.getPanel('sidebar'); - if (sidebarPanel.api.isVisible) { - sidebarPanel.api.setVisible(false); - } else { - event.preventDefault(); // prevent focus - sidebarPanel.api.setVisible(true); - sidebarPanel.focus(); - } - }; - - React.useEffect(() => { - const disposable = new CompositeDisposable( - props.api.onDidActiveChange((event) => { - setActive(event.isActive); - }) - ); - - return () => { - disposable.dispose(); - }; - }, []); - - return ( -
-
- -
-
- -
-
- -
-
- ); -}; diff --git a/packages/dockview-demo/src/layout-grid/application.tsx b/packages/dockview-demo/src/layout-grid/application.tsx deleted file mode 100644 index e4b923787..000000000 --- a/packages/dockview-demo/src/layout-grid/application.tsx +++ /dev/null @@ -1,108 +0,0 @@ -import * as React from 'react'; -import { - Orientation, - GridviewReact, - LayoutPriority, - GridviewReadyEvent, - IGridviewPanelProps, - SerializedGridview, - GridviewApi, -} from 'dockview'; -import { Activitybar } from '../services/widgets'; -import { Footer } from './footer'; -import { Panel } from './panel'; -import { TestGrid } from './layoutGrid'; -import { useLayoutRegistry } from './registry'; -import { Sidebar } from '../services/widgets'; - -const rootcomponents: { - [index: string]: React.FunctionComponent; -} = { - sidebar: Sidebar, - activitybar: Activitybar, - editor: TestGrid, - footer: Footer, - panel: Panel, -}; - -export const Application = () => { - const api = React.useRef(); - const registry = useLayoutRegistry(); - - const onReady = (event: GridviewReadyEvent) => { - api.current = event.api; - registry.register('gridview', event.api); - - let success = false; - const state = localStorage.getItem('dockview-layout'); - - if (state) { - try { - event.api.fromJSON(JSON.parse(state)); - success = true; - } catch (err) { - console.error('failed to load layout', err); - } - } - - if (!success) { - event.api.addPanel({ - id: 'i', - component: 'activitybar', - minimumWidth: 48, - maximumWidth: 48, - location: [0], - }); - - event.api.addPanel({ - id: 'footer', - component: 'footer', - location: [1], - maximumHeight: 22, - minimumHeight: 22, - }); - - event.api.addPanel({ - id: 'editor', - component: 'editor', - snap: true, - location: [0, 1], - priority: LayoutPriority.High, - }); - - event.api.addPanel({ - id: 'sidebar', - component: 'sidebar', - snap: true, - position: { referencePanel: 'editor', direction: 'left' }, - minimumWidth: 170, - size: 100, - }); - - event.api.addPanel({ - id: 'panel', - component: 'panel', - position: { referencePanel: 'editor', direction: 'below' }, - size: 200, - snap: true, - }); - } - - event.api.onDidLayoutChange(() => { - localStorage.setItem( - 'dockview-layout', - JSON.stringify(event.api.toJSON()) - ); - }); - }; - - return ( - - ); -}; diff --git a/packages/dockview-demo/src/layout-grid/controlCenter.scss b/packages/dockview-demo/src/layout-grid/controlCenter.scss deleted file mode 100644 index 115db5777..000000000 --- a/packages/dockview-demo/src/layout-grid/controlCenter.scss +++ /dev/null @@ -1,30 +0,0 @@ -.control-center { - display: flex; - flex-direction: column; - height: 100%; - padding: 4px 8px; - box-sizing: border-box; - - .control-center-row { - height: 25px; - box-sizing: border-box; - - button { - width: 175px; - text-overflow: ellipsis; - overflow: hidden; - white-space: nowrap; - border: none; - background-color: rgb(14, 99, 156); - color: white; - font-family: inherit; - outline: none; - padding: 2px 14px; - margin: 2px 0px; - - &:hover { - background-color: rgb(17, 119, 187); - } - } - } -} diff --git a/packages/dockview-demo/src/layout-grid/controlCenter.tsx b/packages/dockview-demo/src/layout-grid/controlCenter.tsx deleted file mode 100644 index a1b2e1aaa..000000000 --- a/packages/dockview-demo/src/layout-grid/controlCenter.tsx +++ /dev/null @@ -1,150 +0,0 @@ -import * as React from 'react'; -import { DockviewApi, GridviewApi, SplitviewApi } from 'dockview'; -import { useLayoutRegistry } from './registry'; -import './controlCenter.scss'; - -export const ControlCenter = () => { - const registry = useLayoutRegistry(); - - const onAdd = () => { - const api = registry.get('dockview'); - const _id = Date.now(); - const id = `${_id}`; - - api.addPanel({ - component: 'test_component', - id, - title: `Item ${id}`, - }); - }; - - const onAddTheSamePanel = () => { - const api = registry.get('dockview'); - const id = `duplicate_panel`; - - const panel = api.getPanel(id); - if (panel) { - panel.api.setActive(); - return; - } - api.addPanel({ - component: 'test_component', - id, - title: `Item ${id}`, - }); - }; - - const onAddEmpty = () => { - const api = registry.get('dockview'); - api.addEmptyGroup(); - }; - - const nextPanel = (ev: React.MouseEvent) => { - ev.preventDefault(); - const api = registry.get('dockview'); - api.moveToNext({ includePanel: true }); - }; - const nextGroup = (ev: React.MouseEvent) => { - ev.preventDefault(); - const api = registry.get('dockview'); - api.moveToNext(); - }; - const previousPanel = (ev: React.MouseEvent) => { - ev.preventDefault(); - const api = registry.get('dockview'); - api.moveToPrevious({ includePanel: true }); - }; - const previousGroup = (ev: React.MouseEvent) => { - ev.preventDefault(); - const api = registry.get('dockview'); - api.moveToNext(); - }; - - const onConfig = () => { - const api = registry.get('dockview'); - const data = api.toJSON(); - const stringData = JSON.stringify(data, null, 4); - console.log(stringData); - localStorage.setItem('layout', stringData); - }; - - const onLoad = async () => { - const api = registry.get('dockview'); - api.closeAllGroups(); - - const data = localStorage.getItem('layout'); - if (data) { - const jsonData = JSON.parse(data); - api.fromJSON(jsonData); - } - }; - - const onClear = () => { - const api = registry.get('dockview'); - api.closeAllGroups(); - }; - - const saveApplicationLayout = () => { - const api = registry.get('dockview'); - console.log(JSON.stringify(api.toJSON(), null, 4)); - }; - - const onAddSettings = () => { - const api = registry.get('dockview'); - - const settingsPanel = api.getPanel('settings'); - if (settingsPanel) { - settingsPanel.api.setActive(); - return; - } - - api.addPanel({ - id: 'settings', - component: 'settings', - title: 'Settings', - }); - }; - - return ( -
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
- ); -}; diff --git a/packages/dockview-demo/src/layout-grid/customTab.tsx b/packages/dockview-demo/src/layout-grid/customTab.tsx deleted file mode 100644 index 5ba8623f3..000000000 --- a/packages/dockview-demo/src/layout-grid/customTab.tsx +++ /dev/null @@ -1,6 +0,0 @@ -import * as React from 'react'; -import { IGroupPanelBaseProps } from 'dockview'; - -export const CustomTab = (props: IGroupPanelBaseProps) => { - return
hello
; -}; diff --git a/packages/dockview-demo/src/layout-grid/footer.tsx b/packages/dockview-demo/src/layout-grid/footer.tsx deleted file mode 100644 index 1266373a5..000000000 --- a/packages/dockview-demo/src/layout-grid/footer.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import * as React from 'react'; -import { IGridviewPanelProps } from 'dockview'; -import { atom, useRecoilValue } from 'recoil'; - -export const selectedPanelAtom = atom({ - key: 'selectedPanelAtom', - default: '', -}); - -export const Footer = (props: IGridviewPanelProps) => { - const selectedPanel = useRecoilValue(selectedPanelAtom); - - return ( -
- - {selectedPanel} -
- ); -}; diff --git a/packages/dockview-demo/src/layout-grid/layoutGrid.layout.json b/packages/dockview-demo/src/layout-grid/layoutGrid.layout.json deleted file mode 100644 index 83b2f7242..000000000 --- a/packages/dockview-demo/src/layout-grid/layoutGrid.layout.json +++ /dev/null @@ -1,101 +0,0 @@ -{ - "grid": { - "root": { - "type": "branch", - "data": [ - { - "type": "leaf", - "data": { - "id": "group_1", - "views": [ - "split_panel" - ], - "activeView": "split_panel" - }, - "size": 371 - }, - { - "type": "branch", - "data": [ - { - "type": "leaf", - "data": { - "id": "group_2", - "views": [ - "item2" - ], - "activeView": "item2" - }, - "size": 792 - }, - { - "type": "leaf", - "data": { - "id": "group_3", - "views": [ - "panel_2" - ], - "activeView": "panel_2" - }, - "size": 792 - } - ], - "size": 371 - }, - { - "type": "leaf", - "data": { - "id": "group_4", - "views": [ - "panel_0" - ], - "activeView": "panel_0" - }, - "size": 372 - } - ], - "size": 1584 - }, - "height": 1114, - "width": 1584, - "orientation": "HORIZONTAL" - }, - "panels": { - "panel_0": { - "id": "panel_0", - "contentId": "test_component", - "tabId": "__DEFAULT_TAB__", - "props": { - "text": "how low?" - }, - "title": "Item 1", - "state": {} - }, - "item2": { - "id": "item2", - "contentId": "test_component", - "tabId": "__DEFAULT_TAB__", - "props": {}, - "title": "Item 2", - "state": {} - }, - "split_panel": { - "id": "split_panel", - "contentId": "split_panel", - "tabId": "__DEFAULT_TAB__", - "props": {}, - "title": "Item 3 with a long title", - "state": {} - }, - "panel_2": { - "id": "panel_2", - "contentId": "test_component", - "tabId": "__DEFAULT_TAB__", - "props": {}, - "title": "Item 3", - "suppressClosable": true, - "state": {} - } - }, - "activeGroup": "group_2" -} \ No newline at end of file diff --git a/packages/dockview-demo/src/layout-grid/layoutGrid.scss b/packages/dockview-demo/src/layout-grid/layoutGrid.scss deleted file mode 100644 index 29e0d1898..000000000 --- a/packages/dockview-demo/src/layout-grid/layoutGrid.scss +++ /dev/null @@ -1,22 +0,0 @@ -.context-menu { - position: absolute; - height: 100px; - width: 170px; - font-size: 13px; - z-index: 999; - color: rgb(204, 204, 204); - background: rgb(37, 37, 38); - box-shadow: rgb(0, 0, 0) 0px 2px 4px; - padding: 10px 0px; - - .context-action { - height: 20px; - line-height: 20px; - padding: 2px 10px; - cursor: pointer; - - &:hover { - background-color: rgb(51, 51, 51); - } - } -} diff --git a/packages/dockview-demo/src/layout-grid/layoutGrid.tsx b/packages/dockview-demo/src/layout-grid/layoutGrid.tsx deleted file mode 100644 index d393c3030..000000000 --- a/packages/dockview-demo/src/layout-grid/layoutGrid.tsx +++ /dev/null @@ -1,334 +0,0 @@ -import * as React from 'react'; -import * as ReactDOM from 'react-dom'; -import { - IDockviewPanelProps, - IGridviewPanelProps, - TabContextMenuEvent, - DockviewReadyEvent, - DockviewReact, - DockviewApi, - IWatermarkPanelProps, - IDockviewPanel, - PanelCollection, -} from 'dockview'; -import { CustomTab } from './customTab'; -import { Settings } from './settingsPanel'; -import { useLayoutRegistry } from './registry'; -import { SplitPanel } from './splitPanel'; -import './layoutGrid.scss'; -import { WelcomePanel } from '../panels/welcome/welcome'; -import { SplitviewPanel } from '../panels/splitview/splitview'; -import { GridviewDemoPanel } from '../panels/gridview/gridview'; -import { useRecoilCallback } from 'recoil'; -import { selectedPanelAtom } from './footer'; -import { ExampleFunctions } from './panels/exampleFunctions'; -import { CompositeDisposable } from '../lifecycle'; - -const WatermarkComponent = () => { - return ( -
- Watermark component -
- ); -}; - -const Test = (props: IDockviewPanelProps) => { - const [counter, setCounter] = React.useState(0); - - React.useEffect(() => { - const interval = setInterval(() => { - setCounter((_) => _ + 1); - }, 2000); - - return () => { - clearInterval(interval); - }; - }, []); - - return ( -
-
{`custom body ${counter}`}
- -
- ); -}; - -const components: PanelCollection = { - test: Test, - welcome: WelcomePanel, - splitview: SplitviewPanel, - gridview: GridviewDemoPanel, - inner_component: (props: IDockviewPanelProps) => { - const _api = React.useRef(); - - const onReady = (event: DockviewReadyEvent) => { - _api.current = event.api; - - event.api.addPanel({ - component: 'test_component', - id: 'inner-1', - title: 'inner-1', - }); - event.api.addPanel({ - component: 'test_component', - id: 'inner-2', - title: 'inner-2', - }); - event.api.addPanel({ - component: 'test_component', - id: nextGuid(), - title: 'inner-3', - position: { - direction: 'within', - referencePanel: 'inner-1', - }, - }); - event.api.addPanel({ - component: 'test_component', - id: nextGuid(), - title: 'inner-4', - position: { - direction: 'within', - referencePanel: 'inner-2', - }, - }); - }; - - return ( -
- -
- ); - }, - test_component: ExampleFunctions, - settings: Settings, - split_panel: SplitPanel, -}; - -const tabComponents = { - default: CustomTab, -}; - -export const nextGuid = (() => { - let counter = 0; - return () => 'panel_' + (counter++).toString(); -})(); - -export const TestGrid = (props: IGridviewPanelProps) => { - const [api, setApi] = React.useState(); - const registry = useLayoutRegistry(); - - const onReady = (event: DockviewReadyEvent) => { - const api = event.api; - setApi(api); - registry.register('dockview', api); - }; - - const setSelectedPanel = useRecoilCallback( - ({ set }) => - (value: string) => - set(selectedPanelAtom, value), - [] - ); - - React.useEffect(() => { - if (!api) { - return () => { - // - }; - } - props.api.setConstraints({ - minimumWidth: () => api.minimumWidth, - minimumHeight: () => api.minimumHeight, - }); - - const disposable = new CompositeDisposable( - api.onDidLayoutChange(() => { - const state = api.toJSON(); - localStorage.setItem('dockview', JSON.stringify(state)); - }), - api.onDidActivePanelChange((e) => { - setSelectedPanel(e?.id || ''); - }) - ); - - const state = localStorage.getItem('dockview'); - let success = false; - if (state) { - try { - api.fromJSON(JSON.parse(state)); - success = true; - } catch (err) { - console.error('failed to load layout', err); - } - } - - const welcomePanel = api.getPanel('welcome'); - - if (!welcomePanel) { - api.addPanel({ - component: 'welcome', - id: 'welcome', - title: 'Welcome', - suppressClosable: true, - }); - } - - return () => { - disposable.dispose(); - }; - }, [api]); - - const [coord, setCoord] = - React.useState<{ - x: number; - y: number; - panel: IDockviewPanel; - }>(undefined); - - const onTabContextMenu = React.useMemo( - () => (event: TabContextMenuEvent) => { - event.event.preventDefault(); - console.log(event.panel); - const cb = (event: MouseEvent) => { - let element: HTMLElement = event.target as HTMLElement; - - while (element) { - if (element.classList.contains('context-menu')) { - return; - } - element = element.parentElement; - } - - window.removeEventListener('mousedown', cb); - setCoord(undefined); - }; - window.addEventListener('mousedown', cb); - setCoord({ - x: event.event.clientX, - y: event.event.clientY, - panel: event.panel, - }); - }, - [] - ); - - const onClose = () => { - setCoord(undefined); - coord.panel.api.close(); - }; - - const onChangeName = () => { - setCoord(undefined); - coord.panel.api.setTitle('This looks new?'); - }; - - return ( - <> - {coord && - ReactDOM.createPortal( -
-
- Close -
-
- Rename -
-
, - document.getElementById('anchor') - )} - { - return true; - }} - onDidDrop={(ev) => { - console.log('onDidDrop', ev); - }} - /> - - ); -}; - -const Watermark = (props: IWatermarkPanelProps) => { - const [groups, setGroups] = React.useState(props.containerApi.size); - React.useEffect(() => { - const disposable = new CompositeDisposable( - props.containerApi.onDidLayoutChange(() => { - console.log(`groups2 ${props.containerApi.size}`); - setGroups(props.containerApi.size); - }) - ); - - return () => { - disposable.dispose(); - }; - }, []); - - const onClick = () => { - props.close(); - }; - - return ( -
- -
- Watermark component -
-
- ); -}; diff --git a/packages/dockview-demo/src/layout-grid/panel.tsx b/packages/dockview-demo/src/layout-grid/panel.tsx deleted file mode 100644 index b43ad819d..000000000 --- a/packages/dockview-demo/src/layout-grid/panel.tsx +++ /dev/null @@ -1,67 +0,0 @@ -import * as React from 'react'; -import { IGridviewPanelProps } from 'dockview'; -import { CompositeDisposable } from '../lifecycle'; - -export const Panel = (props: IGridviewPanelProps) => { - const [active, setActive] = React.useState(); - const [focused, setFocused] = React.useState(); - - React.useEffect(() => { - const disposable = new CompositeDisposable( - props.api.onDidActiveChange((event) => { - setActive(event.isActive); - }), - props.api.onDidFocusChange((event) => { - setFocused(event.isFocused); - }) - ); - - return () => { - disposable.dispose(); - }; - }, []); - - const onToggle = () => { - const editorPanel = props.containerApi.getPanel('editor'); - editorPanel.api.setVisible(!editorPanel.api.isVisible); - }; - - const onClose = () => { - const editorPanel = props.containerApi.getPanel('editor'); - - editorPanel.api.setVisible(true); - props.api.setVisible(false); - }; - - const onMove = () => { - const thisPanel = props.containerApi.getPanel('panel'); - const editor = props.containerApi.getPanel('editor'); - - props.containerApi.movePanel(thisPanel, { - direction: 'left', - reference: editor.id, - }); - }; - - return ( -
-
- This panel is outside of the dockable layer - - {/* */} - - -
-
-
{`isPanelActive: ${active} isPanelFocused: ${focused}`}
-
-
- ); -}; diff --git a/packages/dockview-demo/src/layout-grid/panels/exampleFunctions.scss b/packages/dockview-demo/src/layout-grid/panels/exampleFunctions.scss deleted file mode 100644 index 663346603..000000000 --- a/packages/dockview-demo/src/layout-grid/panels/exampleFunctions.scss +++ /dev/null @@ -1,14 +0,0 @@ -.example-functions-panel { - padding: 5px; - display: flex; - flex-direction: column; - - .example-functions-panel-header-bar { - display: flex; - justify-content: flex-end; - } - - .example-functions-panel-section { - padding: 8px 0px; - } -} diff --git a/packages/dockview-demo/src/layout-grid/panels/exampleFunctions.tsx b/packages/dockview-demo/src/layout-grid/panels/exampleFunctions.tsx deleted file mode 100644 index b0832f0cf..000000000 --- a/packages/dockview-demo/src/layout-grid/panels/exampleFunctions.tsx +++ /dev/null @@ -1,102 +0,0 @@ -import { IDockviewPanelProps } from 'dockview'; -import * as React from 'react'; -import { CompositeDisposable } from '../../lifecycle'; -import './exampleFunctions.scss'; - -export const ExampleFunctions = ( - props: IDockviewPanelProps & { [key: string]: any } -) => { - const [panelState, setPanelState] = React.useState<{ - isGroupActive: boolean; - isPanelVisible: boolean; - }>({ - isGroupActive: props.api.isActive, - isPanelVisible: props.api.isVisible, - }); - const [panelName, setPanelName] = React.useState(); - const input = React.useRef(); - - React.useEffect(() => { - const disposable = new CompositeDisposable( - props.api.onDidActiveChange((event) => { - setPanelState((_) => ({ - ..._, - isGroupActive: event.isActive, - })); - }), - props.api.onDidVisibilityChange((x) => { - setPanelState((_) => ({ - ..._, - isPanelVisible: x.isVisible, - })); - }), - props.api.onDidFocusChange(({ isFocused }) => { - if (isFocused) { - input.current.focus(); - } - }) - ); - - return () => { - disposable.dispose(); - }; - }, []); - - const onRename = () => { - props.api.setTitle(panelName); - }; - - const onClose = () => { - props.api.close(); - }; - - return ( -
-
- - {'isGroupActive: '} - - {`${panelState.isGroupActive}`} - - - - {'isPanelVisible: '} - - {`${panelState.isPanelVisible}`} - - -
-
- setPanelName(event.target.value)} - /> - -
- - - -
- ); -}; diff --git a/packages/dockview-demo/src/layout-grid/registry.ts b/packages/dockview-demo/src/layout-grid/registry.ts deleted file mode 100644 index bd023f6a9..000000000 --- a/packages/dockview-demo/src/layout-grid/registry.ts +++ /dev/null @@ -1,25 +0,0 @@ -import * as React from 'react'; - -export interface IRegistry { - register(name: string, value: T): void; - get(name: string): T; -} - -class Registry implements IRegistry { - private cache = new Map(); - - register(name: string, value: T) { - this.cache.set(name, value); - } - - get(name: string) { - return this.cache.get(name) as T; - } -} - -const RegistryInstance = new Registry(); - -export const useLayoutRegistry = (): IRegistry => { - const ref = React.useRef(RegistryInstance); - return ref.current; -}; diff --git a/packages/dockview-demo/src/layout-grid/settingsPanel.tsx b/packages/dockview-demo/src/layout-grid/settingsPanel.tsx deleted file mode 100644 index 6704e49a1..000000000 --- a/packages/dockview-demo/src/layout-grid/settingsPanel.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import * as React from 'react'; -import { IDockviewPanelProps } from 'dockview'; - -export const Settings = (props: IDockviewPanelProps) => { - const [tabHeight, setTabHeight] = React.useState( - props.containerApi.getTabHeight() - ); - - const onTabHeightChange = (event: React.ChangeEvent) => { - const value = Number(event.target.value); - if (!Number.isNaN(value)) { - setTabHeight(value); - } - }; - - const onClick = () => { - props.containerApi.setTabHeight(tabHeight); - }; - - const onRemove = () => { - props.containerApi.setTabHeight(undefined); - }; - - return ( -
- -
- ); -}; diff --git a/packages/dockview-demo/src/layout-grid/sidebar.layout.json b/packages/dockview-demo/src/layout-grid/sidebar.layout.json deleted file mode 100644 index 5dd7eb9ba..000000000 --- a/packages/dockview-demo/src/layout-grid/sidebar.layout.json +++ /dev/null @@ -1,57 +0,0 @@ -{ - "views": [ - { - "size": 22, - "data": { - "id": "1", - "component": "controlCenter", - - "props": {}, - "title": "Control Center", - "state": {} - }, - "minimumSize": 120 - }, - { - "size": 508, - "data": { - "id": "2", - "component": "default", - - "props": {}, - "title": "Panel 1", - "state": {} - }, - "minimumSize": 120, - "expanded": true - }, - { - "size": 248, - "data": { - "id": "3", - "component": "default", - - "props": {}, - "title": "Panel 2", - "state": {} - }, - "minimumSize": 120, - "expanded": true - }, - { - "size": 22, - "data": { - "id": "4", - "component": "default", - - "props": {}, - "title": "Panel 3", - "state": {} - }, - "minimumSize": 120, - "expanded": false - } - ], - "size": 800, - "orientation": "VERTICAL" -} \ No newline at end of file diff --git a/packages/dockview-demo/src/layout-grid/sidebar.scss b/packages/dockview-demo/src/layout-grid/sidebar.scss deleted file mode 100644 index db6ff0c71..000000000 --- a/packages/dockview-demo/src/layout-grid/sidebar.scss +++ /dev/null @@ -1,18 +0,0 @@ -.pane-header { - .my-header { - .actions { - display: none; - } - &.within { - .actions { - display: flex; - } - } - } - - &:focus { - .actions { - display: flex; - } - } -} diff --git a/packages/dockview-demo/src/layout-grid/sidebar.tsx b/packages/dockview-demo/src/layout-grid/sidebar.tsx deleted file mode 100644 index bdb69f55d..000000000 --- a/packages/dockview-demo/src/layout-grid/sidebar.tsx +++ /dev/null @@ -1,97 +0,0 @@ -import * as React from 'react'; -import { - IGridviewPanelProps, - PaneviewReact, - PaneviewReadyEvent, - IPaneviewPanelProps, - PaneviewApi, - PaneviewDropEvent, -} from 'dockview'; -import { ControlCenter } from './controlCenter'; -import './sidebar.scss'; -import { CompositeDisposable } from '../lifecycle'; - -const components = { - default: (props: IPaneviewPanelProps) => { - return
This is an example panel
; - }, - controlCenter: ControlCenter, -}; - -export const Sidebar = (props: IGridviewPanelProps) => { - const api = React.useRef(); - - const onReady = (event: PaneviewReadyEvent) => { - api.current = event.api; - - console.log(props.api.width, props.api.height); - - event.api.fromJSON(require('./sidebar.layout.json')); - - return; - - event.api.addPanel({ - id: '1', - component: 'controlCenter', - title: 'Control Center', - }); - event.api.addPanel({ - id: '2', - component: 'default', - title: 'Panel 1', - }); - event.api.addPanel({ - id: '3', - component: 'default', - title: 'Panel 2', - }); - event.api.addPanel({ - id: '4', - component: 'default', - title: 'Panel 3', - }); - - setTimeout(() => { - console.log(JSON.stringify(event.api.toJSON(), null, 4)); - }, 10000); - }; - - React.useEffect(() => { - const disposable = new CompositeDisposable( - props.api.onDidDimensionsChange((ev) => { - api.current.layout(ev.width, ev.height); - }), - props.api.onDidVisibilityChange((event) => { - console.log(event); - }), - props.api.onDidFocusChange(({ isFocused }) => { - if (isFocused) { - api.current.focus(); - } - }) - ); - - return () => { - disposable.dispose(); - }; - }, []); - - const onDidDrop = React.useCallback((event: PaneviewDropEvent) => { - console.log('drop', event); - }, []); - - return ( -
- -
- ); -}; diff --git a/packages/dockview-demo/src/layout-grid/splitPanel.scss b/packages/dockview-demo/src/layout-grid/splitPanel.scss deleted file mode 100644 index e69de29bb..000000000 diff --git a/packages/dockview-demo/src/layout-grid/splitPanel.tsx b/packages/dockview-demo/src/layout-grid/splitPanel.tsx deleted file mode 100644 index c5b812383..000000000 --- a/packages/dockview-demo/src/layout-grid/splitPanel.tsx +++ /dev/null @@ -1,145 +0,0 @@ -import * as React from 'react'; -import { - ISplitviewPanelProps, - Orientation, - SplitviewReadyEvent, - SplitviewReact, - SplitviewApi, - IDockviewPanelProps, -} from 'dockview'; -import { useLayoutRegistry } from './registry'; -import './splitPanel.scss'; -import { CompositeDisposable } from '../lifecycle'; - -const components = { - default1: (props: ISplitviewPanelProps) => { - const ref = React.useRef(); - const [focused, setFocused] = React.useState(false); - const [active, setActive] = React.useState(false); - - const onClick = (ev: React.MouseEvent) => { - props.api.setSize({ size: 300 }); - }; - - React.useEffect(() => { - const disposable = new CompositeDisposable( - props.api.onDidFocusChange((event) => { - setFocused(event.isFocused); - }), - props.api.onDidActiveChange((event) => { - setActive(event.isActive); - }), - props.api.onDidFocusChange(({ isFocused }) => { - if (isFocused) { - ref.current.focus(); - } - }) - ); - - return () => { - disposable.dispose(); - }; - }, []); - - return ( -
-
-
This is a splitview panel inside a dockable panel
- - -
-
-
{`isPanelActive: ${active} isPanelFocused: ${focused}`}
- - {(props as any).text} -
-
- ); - }, -}; - -export const SplitPanel = (props: IDockviewPanelProps) => { - const api = React.useRef(); - const registry = useLayoutRegistry(); - - React.useEffect(() => { - const disposable = new CompositeDisposable( - props.api.onDidDimensionsChange((event) => { - api.current?.layout(event.width, event.height - 25); - }), - props.api.onDidFocusChange(({ isFocused }) => { - if (isFocused) { - api.current.focus(); - } - }) - ); - - return () => { - disposable.dispose(); - }; - }, []); - - const onReady = (event: SplitviewReadyEvent) => { - api.current = event.api; - - registry.register('splitview', event.api); - - event.api.fromJSON(require('./splitpanel.layout.json')); - return; - event.api.addPanel({ - id: '1', - component: 'default1', - snap: true, - params: { - text: 'hiya', - }, - }); - event.api.addPanel({ id: '2', component: 'default1' }); - }; - - const onUpdateProps = () => { - const panel = api.current.getPanel('1'); - panel.update({ params: { text: Date.now().toString() } }); - }; - - const onAdd = () => { - api.current.addPanel({ - id: `${Date.now()}`, - component: 'default1', - snap: true, - params: { - text: 'hiya', - }, - }); - }; - - const onRemove = () => { - const panels = api.current.panels; - if (panels.length === 0) { - return; - } - api.current.removePanel(panels[panels.length - 1]); - }; - - return ( -
-
- - - -
- -
- ); -}; diff --git a/packages/dockview-demo/src/layout-grid/splitpanel.layout.json b/packages/dockview-demo/src/layout-grid/splitpanel.layout.json deleted file mode 100644 index fa01f2ef9..000000000 --- a/packages/dockview-demo/src/layout-grid/splitpanel.layout.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "views": [ - { - "size": 200, - "data": { - "id": "1", - "component": "default1", - "props": { - "text": "hiya" - } - }, - "minimumSize": 200, - "maximumSize": 9007199254740991, - "snap": true - }, - { - "size": 409, - "data": { - "id": "2", - "component": "default1" - }, - "minimumSize": 200, - "maximumSize": 9007199254740991 - } - ], - "activeView": "2", - "size": 609, - "orientation": "VERTICAL" -} \ No newline at end of file diff --git a/packages/dockview-demo/src/lifecycle.ts b/packages/dockview-demo/src/lifecycle.ts deleted file mode 100644 index 0486cedff..000000000 --- a/packages/dockview-demo/src/lifecycle.ts +++ /dev/null @@ -1,54 +0,0 @@ -export interface IDisposable { - dispose: () => void; -} - -export interface IValueDisposable { - value: T; - disposable: IDisposable; -} - -export namespace Disposable { - export const NONE: IDisposable = { - dispose: () => { - // noop - }, - }; -} - -export class CompositeDisposable { - private readonly disposables: IDisposable[]; - - public static from(...args: IDisposable[]) { - return new CompositeDisposable(...args); - } - - constructor(...args: IDisposable[]) { - this.disposables = args; - } - - public addDisposables(...args: IDisposable[]) { - args.forEach((arg) => this.disposables.push(arg)); - } - - public dispose() { - this.disposables.forEach((arg) => arg.dispose()); - } -} - -export class MutableDisposable implements IDisposable { - private _disposable = Disposable.NONE; - - set value(disposable: IDisposable) { - if (this._disposable) { - this._disposable.dispose(); - } - this._disposable = disposable; - } - - public dispose() { - if (this._disposable) { - this._disposable.dispose(); - this._disposable = Disposable.NONE; - } - } -} diff --git a/packages/dockview-demo/src/panels/gridview/gridview.tsx b/packages/dockview-demo/src/panels/gridview/gridview.tsx deleted file mode 100644 index 7c1b85219..000000000 --- a/packages/dockview-demo/src/panels/gridview/gridview.tsx +++ /dev/null @@ -1,328 +0,0 @@ -import { - ActiveEvent, - FocusEvent, - GridConstraintChangeEvent, - GridviewApi, - GridviewReact, - GridviewReadyEvent, - IDockviewPanelProps, - IGridviewPanelProps, - LayoutPriority, - Orientation, - orthogonal, - PanelCollection, - PanelDimensionChangeEvent, - VisibilityEvent, -} from 'dockview'; -import * as React from 'react'; -import { CompositeDisposable } from '../../lifecycle'; - -const components: PanelCollection = { - default: (props) => { - const [active, setActive] = React.useState(false); - const [visible, setVisible] = React.useState(false); - const [focused, setFocused] = React.useState(false); - const [dimension, setDimension] = React.useState<{ - width: number; - height: number; - }>({ width: 0, height: 0 }); - const [ - constraints, - setConstraints, - ] = React.useState({ - minimumHeight: undefined, - maximumHeight: undefined, - minimumWidth: undefined, - maximumWidth: undefined, - }); - - React.useEffect(() => { - const disposable = new CompositeDisposable( - props.api.onDidActiveChange((event: ActiveEvent) => { - setActive(event.isActive); - }), - props.api.onDidConstraintsChange( - (event: GridConstraintChangeEvent) => { - setConstraints(event); - } - ), - props.api.onDidDimensionsChange( - (event: PanelDimensionChangeEvent) => { - setDimension(event); - } - ), - props.api.onDidFocusChange((event: FocusEvent) => { - setFocused(event.isFocused); - }), - props.api.onDidVisibilityChange((event: VisibilityEvent) => { - setVisible(event.isVisible); - }) - ); - - return () => { - disposable.dispose(); - }; - }, []); - - const color = React.useMemo( - () => - `rgb(${Math.floor(256 * Math.random())},${Math.floor( - 256 * Math.random() - )},${Math.floor(256 * Math.random())})`, - [] - ); - - return ( -
-
- Width - {dimension.width} - Height - {dimension.height} - Min. height - {constraints.minimumHeight} - Max. height - {constraints.maximumHeight} - Min. width - {constraints.minimumWidth} - Max. width - {constraints.maximumWidth} - Active - {active.toString()} - Visible - {visible.toString()} - Focused - {focused.toString()} -
-
- ); - }, -}; - -export const GridviewDemoPanel = (props: IDockviewPanelProps) => { - return ( -
-
-

Gridview

-
-
    -
  • - The gridview is a collection of nested splitviews which - forms a grid-based layout -
  • -
- -
- ); -}; - -export const GridviewDemo = (props: IDockviewPanelProps) => { - const api = React.useRef(); - - const [orientation, setOrientation] = React.useState( - Orientation.VERTICAL - ); - - const onClick = () => { - api.current.orientation = orthogonal(api.current.orientation); - // load(); - }; - - const load = () => { - api.current.fromJSON({ - activePanel: '1', - grid: { - height: 3, - width: 2, - orientation: orientation, - root: { - type: 'branch', - size: 3, - data: [ - { - type: 'branch', - size: 1, - data: [ - { - type: 'leaf', - size: 1, - data: { - id: '1', - component: 'default', - minimumHeight: 50, - maximumHeight: Number.POSITIVE_INFINITY, - minimumWidth: 50, - maximumWidth: Number.POSITIVE_INFINITY, - snap: false, - priority: LayoutPriority.Normal, - }, - }, - { - type: 'branch', - size: 1, - data: [ - { - type: 'leaf', - size: 0.5, - data: { - id: '2', - component: 'default', - minimumHeight: 50, - maximumHeight: - Number.POSITIVE_INFINITY, - minimumWidth: 50, - maximumWidth: - Number.POSITIVE_INFINITY, - snap: false, - priority: LayoutPriority.Normal, - }, - }, - { - type: 'branch', - size: 0.5, - data: [ - { - type: 'leaf', - size: 0.5, - data: { - id: '2', - component: 'default', - minimumHeight: 50, - maximumHeight: - Number.POSITIVE_INFINITY, - minimumWidth: 50, - maximumWidth: - Number.POSITIVE_INFINITY, - snap: false, - priority: - LayoutPriority.Normal, - }, - }, - { - type: 'leaf', - size: 0.5, - data: { - id: '3', - component: 'default', - minimumHeight: 50, - maximumHeight: - Number.POSITIVE_INFINITY, - minimumWidth: 50, - maximumWidth: - Number.POSITIVE_INFINITY, - snap: false, - priority: - LayoutPriority.Normal, - }, - }, - ], - }, - ], - }, - ], - }, - { - type: 'leaf', - size: 1, - data: { - id: '4', - component: 'default', - minimumHeight: 50, - maximumHeight: Number.POSITIVE_INFINITY, - minimumWidth: 50, - maximumWidth: Number.POSITIVE_INFINITY, - snap: false, - priority: LayoutPriority.Normal, - }, - }, - { - type: 'branch', - size: 1, - data: [ - { - type: 'leaf', - size: 1, - data: { - id: '3', - component: 'default', - minimumHeight: 50, - maximumHeight: Number.POSITIVE_INFINITY, - minimumWidth: 50, - maximumWidth: Number.POSITIVE_INFINITY, - snap: false, - priority: LayoutPriority.Normal, - }, - }, - { - type: 'leaf', - size: 1, - data: { - id: '4', - component: 'default', - minimumHeight: 50, - maximumHeight: Number.POSITIVE_INFINITY, - minimumWidth: 50, - maximumWidth: Number.POSITIVE_INFINITY, - snap: false, - priority: LayoutPriority.Normal, - }, - }, - ], - }, - ], - }, - }, - }); - }; - - const onReady = (event: GridviewReadyEvent) => { - api.current = event.api; - api.current?.layout(props.api.width - 80, 600); - - load(); - }; - - React.useEffect(() => { - const disposable = new CompositeDisposable( - props.api.onDidDimensionsChange((event) => { - api.current?.layout(event.width - 80, 600); - }) - ); - - return () => { - disposable.dispose(); - }; - }, []); - - return ( -
- -
- -
-
- ); -}; diff --git a/packages/dockview-demo/src/panels/splitview/splitview.scss b/packages/dockview-demo/src/panels/splitview/splitview.scss deleted file mode 100644 index e395ca0b1..000000000 --- a/packages/dockview-demo/src/panels/splitview/splitview.scss +++ /dev/null @@ -1,76 +0,0 @@ -.splitview-demo-container { - width: 100%; - padding: 0px 20px; - box-sizing: border-box; - - .splitview-demo-content { - display: flex; - --dv-separator-border: white; - } - - &.vertical { - .api-parameter { - padding-left: 10px; - } - - .splitview-demo-view { - height: 500px; - width: 150px; - - .api-parameter { - padding-left: 0px; - } - } - } - &.horizontal { - .splitview-demo-content { - flex-direction: column; - } - - .api-parameter { - padding-top: 10px; - } - - .splitview-demo-view { - height: 180px; - width: 500px; - - .api-parameter { - padding-top: 0px; - } - } - } -} - -.api-parameter { - display: grid; - grid-template-columns: 70px 50px; - grid-auto-rows: 18px; - - span { - &:nth-child(2n) { - text-align: right; - } - } - - .visibility-toggle { - height: 16px; - outline: 1px solid dodgerblue; - outline-offset: -1px; - padding: 0px 4px; - display: flex; - cursor: pointer; - - &:not(:first-child) { - margin-left: 4px; - } - } -} - -.splitview-demo-panel { - background-color: #2d2d2d; - height: 100%; - padding: 10px; - box-sizing: border-box; - overflow: hidden; -} diff --git a/packages/dockview-demo/src/panels/splitview/splitview.tsx b/packages/dockview-demo/src/panels/splitview/splitview.tsx deleted file mode 100644 index 17012f142..000000000 --- a/packages/dockview-demo/src/panels/splitview/splitview.tsx +++ /dev/null @@ -1,308 +0,0 @@ -import { - ISplitviewPanelProps, - Orientation, - SplitviewApi, - SplitviewReact, - SplitviewReadyEvent, - PanelDimensionChangeEvent, - ActiveEvent, - FocusEvent, - VisibilityEvent, - PanelConstraintChangeEvent, - IDockviewPanelProps, -} from 'dockview'; -import * as React from 'react'; -import { CompositeDisposable } from '../../lifecycle'; -import './splitview.scss'; - -const components = { - default: (props: ISplitviewPanelProps) => { - const [active, setActive] = React.useState(false); - const [visible, setVisible] = React.useState(false); - const [focused, setFocused] = React.useState(false); - const [dimension, setDimension] = React.useState<{ - width: number; - height: number; - }>({ width: 0, height: 0 }); - const [constraints, setConstraints] = React.useState<{ - maximumSize?: number; - minimumSize?: number; - }>({ maximumSize: undefined, minimumSize: undefined }); - - React.useEffect(() => { - const disposable = new CompositeDisposable( - props.api.onDidActiveChange((event: ActiveEvent) => { - setActive(event.isActive); - }), - props.api.onDidConstraintsChange( - (event: PanelConstraintChangeEvent) => { - setConstraints(event); - } - ), - props.api.onDidDimensionsChange( - (event: PanelDimensionChangeEvent) => { - setDimension(event); - } - ), - props.api.onDidFocusChange((event: FocusEvent) => { - setFocused(event.isFocused); - }), - props.api.onDidVisibilityChange((event: VisibilityEvent) => { - setVisible(event.isVisible); - }) - ); - - return () => { - disposable.dispose(); - }; - }, []); - - const color = React.useMemo( - () => 'rgba(14, 99, 156,0.4)', - // `rgb(${Math.floor(256 * Math.random())},${Math.floor( - // 256 * Math.random() - // )},${Math.floor(256 * Math.random())})`, - [] - ); - - return ( -
-
- Id - {props.api.id} - Width - {dimension.width} - Height - {dimension.height} - Min. size - {constraints.minimumSize} - Max. size - {constraints.maximumSize} - Active - {active.toString()} - Visible - {visible.toString()} - Focused - {focused.toString()} -
-
- ); - }, -}; - -export const SplitviewPanel = (props: IDockviewPanelProps) => { - return ( -
-
- Splitview -
-
    -
  • - The splitview component exposes an API object, a selection - of avaliable values are shown in the summary sections below -
  • -
  • - Each panel exposes it's own API, and has access to the - common API. A selector of panel API values are shown in each - panel of the splitview. -
  • -
- - -
- ); -}; - -export const Common = ( - props: IDockviewPanelProps & { orientation: Orientation } -) => { - const api = React.useRef(); - - const [dimensions, setDimensions] = React.useState<{ - height: number; - width: number; - maximumSize: number; - minimumSize: number; - visibility: boolean[]; - length: number; - }>({ - height: undefined, - width: undefined, - maximumSize: undefined, - minimumSize: undefined, - visibility: [], - length: undefined, - }); - - React.useEffect(() => { - const disposable = new CompositeDisposable( - props.api.onDidDimensionsChange( - (event: PanelDimensionChangeEvent) => { - switch (props.orientation) { - case Orientation.HORIZONTAL: - api.current.layout(500, 100); - break; - case Orientation.VERTICAL: - api.current.layout(100, 500); - break; - } - - const height = api.current.height; - const width = api.current.width; - const maximumSize = api.current.maximumSize; - const minimumSize = api.current.minimumSize; - const length = api.current.length; - - setDimensions({ - height, - width, - maximumSize, - minimumSize, - length, - visibility: api.current.panels.map( - (_) => _.api.isVisible - ), - }); - } - ), - api.current.onDidLayoutChange(() => { - // - }) - ); - return () => { - disposable.dispose(); - }; - }, []); - - const onReady = (event: SplitviewReadyEvent) => { - api.current = event.api; - - event.api.fromJSON({ - views: [ - { - data: { - id: 'one', - component: 'default', - minimumSize: 10, - }, - size: 1, - }, - { - data: { - id: 'two', - component: 'default', - minimumSize: 10, - maximumSize: 200, - }, - size: 2, - }, - { - data: { - id: 'three', - component: 'default', - minimumSize: 50, - }, - size: 3, - snap: true, - }, - ], - size: 6, - activeView: 'one', - orientation: props.orientation, - }); - }; - - const toggleVisibility = (i: number) => () => { - const panel = api.current.panels[i]; - panel.api.setVisible(panel.api.isVisible); - setDimensions((dimensions) => ({ - ...dimensions, - visibility: api.current.panels.map((_) => _.api.isVisible), - })); - }; - - const move = () => { - api.current.movePanel(api.current.panels.length - 1, 0); - setDimensions((dimensions) => ({ - ...dimensions, - visibility: api.current.panels.map((_) => _.api.isVisible), - })); - }; - - const text = React.useMemo(() => { - switch (props.orientation) { - case Orientation.VERTICAL: - return 'Vertical Splitview'; - case Orientation.HORIZONTAL: - return 'Horizontal Splitview'; - } - }, [props.orientation]); - - return ( -
-

{text}

-
-
-
- -
-
-
- Height - {dimensions.height} - Width - {dimensions.width} - Lenght - {dimensions.length} - Min. size - {dimensions.minimumSize} - Max. size - {dimensions.maximumSize} - Visible - - {dimensions.visibility.map((_, i) => { - return ( -
- {_ ? 'Yes' : 'No'} -
- ); - })} -
- Move view - - Go - -
-
-
- ); -}; diff --git a/packages/dockview-demo/src/panels/welcome/welcome.scss b/packages/dockview-demo/src/panels/welcome/welcome.scss deleted file mode 100644 index f2f76d641..000000000 --- a/packages/dockview-demo/src/panels/welcome/welcome.scss +++ /dev/null @@ -1,33 +0,0 @@ -.welcome-panel { - color: #cccccc; - margin: 40px; - - .welcome-header { - margin-bottom: 40px; - h1 { - font-size: 30px; - } - h2 { - font-size: 20px; - } - } - - .directory { - .directory-title { - font-size: 16px; - height: 25px; - line-height: 25px; - } - .directory-item { - font-size: 13px; - height: 20px; - line-height: 20px; - color: dodgerblue; - cursor: pointer; - - &:hover { - text-decoration: underline; - } - } - } -} diff --git a/packages/dockview-demo/src/panels/welcome/welcome.tsx b/packages/dockview-demo/src/panels/welcome/welcome.tsx deleted file mode 100644 index cfd688f3b..000000000 --- a/packages/dockview-demo/src/panels/welcome/welcome.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import { IDockviewPanelProps } from 'dockview'; -import * as React from 'react'; -import './welcome.scss'; - -export const WelcomePanel = (props: IDockviewPanelProps) => { - const onAddSplitview = (event: React.MouseEvent) => { - const splitviewPanel = props.containerApi.getPanel('splitview'); - if (splitviewPanel) { - splitviewPanel.api.setActive(); - return; - } - - props.containerApi.addPanel({ - id: 'splitview', - component: 'splitview', - title: 'Splitview Docs', - }); - }; - - const onAddGridview = (event: React.MouseEvent) => { - const splitviewPanel = props.containerApi.getPanel('gridview'); - if (splitviewPanel) { - splitviewPanel.api.setActive(); - return; - } - - props.containerApi.addPanel({ - id: 'gridview', - component: 'gridview', - title: 'Gridview Docs', - }); - }; - - return ( -
-
-

Dockview

-

Zero dependency layout manager

-
-
-
Components
-
Dockview
-
- Splitview -
-
- Gridview -
-
Paneview
-
-
- ); -}; diff --git a/packages/dockview-demo/src/resources/dockview-logo.svg b/packages/dockview-demo/src/resources/dockview-logo.svg deleted file mode 100644 index fb328fcab..000000000 --- a/packages/dockview-demo/src/resources/dockview-logo.svg +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/packages/dockview-demo/src/resources/gridview-logo.svg b/packages/dockview-demo/src/resources/gridview-logo.svg deleted file mode 100644 index a305ecb88..000000000 --- a/packages/dockview-demo/src/resources/gridview-logo.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/packages/dockview-demo/src/resources/logo.svg b/packages/dockview-demo/src/resources/logo.svg deleted file mode 100644 index 579c20c82..000000000 --- a/packages/dockview-demo/src/resources/logo.svg +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/packages/dockview-demo/src/resources/paneview-logo.svg b/packages/dockview-demo/src/resources/paneview-logo.svg deleted file mode 100644 index 0e9ac485e..000000000 --- a/packages/dockview-demo/src/resources/paneview-logo.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/packages/dockview-demo/src/resources/splitview-logo.svg b/packages/dockview-demo/src/resources/splitview-logo.svg deleted file mode 100644 index cfa51c489..000000000 --- a/packages/dockview-demo/src/resources/splitview-logo.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/packages/dockview-demo/src/services/sidebarItem.tsx b/packages/dockview-demo/src/services/sidebarItem.tsx deleted file mode 100644 index 97496b740..000000000 --- a/packages/dockview-demo/src/services/sidebarItem.tsx +++ /dev/null @@ -1,144 +0,0 @@ -import { ViewContainer } from './viewContainer'; -import * as React from 'react'; - -export const Container = (props: { - container: ViewContainer; - isActive: boolean; - onDragOver: (e: React.DragEvent) => void; - onDrop: (e: React.DragEvent, direction: 'top' | 'bottom') => void; - onClick: (e: React.MouseEvent) => void; -}) => { - const ref = React.useRef(null); - const [selection, setSelection] = React.useState< - 'top' | 'bottom' | undefined - >(undefined); - const isDragging = React.useRef(false); - - const [dragEntered, setDragEntered] = React.useState(false); - - const timer = React.useRef(null); - - const onDragOver = (e: React.DragEvent) => { - if (!timer.current) { - timer.current = setTimeout(() => { - props.onDragOver(e); - }, 1000); - } - - if (isDragging.current) { - return; - } - - setDragEntered(true); - - e.preventDefault(); - - const target = e.target as HTMLDivElement; - - const width = target.clientWidth; - const height = target.clientHeight; - - if (width === 0 || height === 0) { - return; // avoid div!0 - } - - const x = e.nativeEvent.offsetX; - const y = e.nativeEvent.offsetY; - const xp = (100 * x) / width; - const yp = (100 * y) / height; - - const isTop = yp < 50; - const isBottom = yp >= 50; - - setSelection(isTop ? 'top' : 'bottom'); - }; - - const onDragLeave = (e: React.DragEvent) => { - if (timer.current) { - clearTimeout(timer.current); - timer.current = null; - } - - if (isDragging.current) { - return; - } - - setDragEntered(false); - - setSelection(undefined); - }; - - const onDrop = (e: React.DragEvent) => { - if (timer.current) { - clearTimeout(timer.current); - timer.current = null; - } - if (isDragging.current) { - return; - } - - setDragEntered(false); - - props.onDrop(e, selection); - - setSelection(undefined); - }; - - const onDragEnter = (e: React.DragEvent) => { - e.preventDefault(); - }; - - const onDragStart = (e: React.DragEvent) => { - isDragging.current = true; - - e.dataTransfer.setData( - 'application/json', - JSON.stringify({ container: props.container.id }) - ); - }; - - const onDragEnd = (e: React.DragEvent) => { - isDragging.current = false; - - setDragEntered(false); - }; - - return ( -
- {dragEntered && ( - - ); -}; diff --git a/packages/dockview-demo/src/services/view.ts b/packages/dockview-demo/src/services/view.ts deleted file mode 100644 index 867cf85a0..000000000 --- a/packages/dockview-demo/src/services/view.ts +++ /dev/null @@ -1,64 +0,0 @@ -export interface SerializedView { - id: string; - isExpanded: boolean; -} - -export type ViewOptions = { - id: string; - title: string; - isExpanded: boolean; - isLocationEditable: boolean; - icon: string; -}; - -export interface View { - readonly id: string; - readonly isExpanded: boolean; - readonly title: string; - readonly isLocationEditable: boolean; - readonly icon: string; - toJSON(): SerializedView; -} - -export class DefaultView implements View { - private readonly _id: string; - private readonly _title: string; - private readonly _isExpanded: boolean; - private readonly _isLocationEditable: boolean; - private readonly _icon: string; - - get id(): string { - return this._id; - } - - get title(): string { - return this._title; - } - - get isExpanded(): boolean { - return this._isExpanded; - } - - get isLocationEditable(): boolean { - return this._isLocationEditable; - } - - get icon(): string { - return this._icon; - } - - constructor(options: ViewOptions) { - this._id = options.id; - this._title = options.title; - this._isExpanded = options.isExpanded; - this._isLocationEditable = options.isLocationEditable; - this._icon = options.icon; - } - - toJSON(): SerializedView { - return { - id: this.id, - isExpanded: this.isExpanded, - }; - } -} diff --git a/packages/dockview-demo/src/services/viewContainer.ts b/packages/dockview-demo/src/services/viewContainer.ts deleted file mode 100644 index b622039bb..000000000 --- a/packages/dockview-demo/src/services/viewContainer.ts +++ /dev/null @@ -1,126 +0,0 @@ -import { SerializedPaneview } from 'dockview'; -import { Emitter, Event } from '../events'; -import { DefaultView, View, SerializedView } from './view'; -import { IViewRegistry } from './viewRegistry'; - -export interface SerializedViewContainer { - readonly id: string; - readonly views: SerializedView[]; -} - -export interface ViewContainer { - readonly id: string; - readonly views: View[]; - readonly schema: T | any; - readonly icon: string; - readonly onDidAddView: Event<{ view: View; index?: number }>; - readonly onDidRemoveView: Event; - addView(view: View, location?: number): void; - layout(schema: T): void; - removeView(view: View): void; - clear(): void; - toJSON(): SerializedViewContainer; -} - -export class PaneviewContainer implements ViewContainer { - private readonly _id: string; - private readonly _views: View[] = []; - - private readonly _onDidAddView = new Emitter<{ - view: View; - index?: number; - }>(); - readonly onDidAddView = this._onDidAddView.event; - private readonly _onDidRemoveView = new Emitter(); - readonly onDidRemoveView = this._onDidRemoveView.event; - - private _schema: SerializedPaneview | undefined; - - get id() { - return this._id; - } - - get views() { - return [...this._views]; - } - - get schema(): SerializedPaneview | undefined { - if (!this._schema) { - this._schema = JSON.parse( - localStorage.getItem(`viewcontainer_${this.id}`) - ); - } - return this._schema; - } - - get icon(): string { - const defaultIcon = 'search'; - if (this.views.length > 0) { - return this.views.find((v) => !!v.icon)?.icon || defaultIcon; - } - return defaultIcon; - } - - constructor( - id: string, - viewRegistry: IViewRegistry, - views?: SerializedView[] - ) { - this._id = id; - - if (views) { - for (const view of views) { - const registeredView = viewRegistry.getRegisteredView(view.id); - this.addView( - new DefaultView({ - id: view.id, - title: registeredView.title, - isExpanded: view.isExpanded, - isLocationEditable: registeredView.isLocationEditable, - icon: registeredView.icon, - }) - ); - } - } - // this.addDisposables(this._onDidAddView, this._onDidRemoveView); - } - - layout(schema: SerializedPaneview): void { - this._schema = schema; - localStorage.setItem( - `viewcontainer_${this.id}`, - JSON.stringify(schema) - ); - } - - addView(view: View, index?: number): void { - this._views.splice(index, 0, view); - this._onDidAddView.fire({ view, index }); - } - - removeView(view: View): void { - const index = this._views.indexOf(view); - if (index < 0) { - throw new Error('invalid'); - } - this._views.splice(index, 1); - - if (this._schema) { - this._schema = { ...this._schema }; - this._schema.views = this._schema.views.filter( - (v) => v.data.id !== view.id - ); - this.layout(this._schema); - } - - this._onDidRemoveView.fire(view); - } - - clear() { - localStorage.removeItem(`viewcontainer_${this.id}`); - } - - toJSON(): SerializedViewContainer { - return { id: this.id, views: this.views.map((v) => v.toJSON()) }; - } -} diff --git a/packages/dockview-demo/src/services/viewRegistry.tsx b/packages/dockview-demo/src/services/viewRegistry.tsx deleted file mode 100644 index 6666e417b..000000000 --- a/packages/dockview-demo/src/services/viewRegistry.tsx +++ /dev/null @@ -1,94 +0,0 @@ -import React from 'react'; -import { ControlCenter } from '../layout-grid/controlCenter'; - -export interface RegisteredView { - id: string; - icon: string; - title: string; - isLocationEditable: boolean; - component: React.FunctionComponent; -} - -export interface IViewRegistry { - getRegisteredView(id: string): RegisteredView | undefined; -} - -export class ViewRegistry { - private readonly _registry = new Map(); - - register(registeredView: RegisteredView): void { - this._registry.set(registeredView.id, registeredView); - } - - getRegisteredView(id: string): RegisteredView | undefined { - return this._registry.get(id); - } -} - -export const VIEW_REGISTRY = new ViewRegistry(); - -VIEW_REGISTRY.register({ - id: 'search_widget', - title: 'search', - icon: 'search', - isLocationEditable: false, - component: () => { - return ( -
- This is a search bar component -
- ); - }, -}); -VIEW_REGISTRY.register({ - id: 'home_widget', - title: 'Home', - icon: 'home', - isLocationEditable: true, - component: ControlCenter, -}); - -VIEW_REGISTRY.register({ - id: 'account_widget', - title: 'Account', - icon: 'account_circle', - isLocationEditable: true, - component: () => { - return ( -
- account_circle -
- ); - }, -}); -VIEW_REGISTRY.register({ - id: 'settings_widget', - title: 'Settings', - icon: 'settings', - isLocationEditable: true, - component: () => { - return ( -
- settings -
- ); - }, -}); diff --git a/packages/dockview-demo/src/services/viewService.ts b/packages/dockview-demo/src/services/viewService.ts deleted file mode 100644 index cf5bdacee..000000000 --- a/packages/dockview-demo/src/services/viewService.ts +++ /dev/null @@ -1,213 +0,0 @@ -import { Emitter, Event } from '../events'; -import { IDisposable } from '../lifecycle'; -import { View } from './view'; -import { - PaneviewContainer, - ViewContainer, - SerializedViewContainer, -} from './viewContainer'; -import { IViewRegistry } from './viewRegistry'; - -export interface SerializedViewService { - containers: SerializedViewContainer[]; - activeContainer?: string; -} - -export interface IViewService extends IDisposable { - readonly containers: ViewContainer[]; - readonly onDidActiveContainerChange: Event; - readonly onDidRemoveContainer: Event; - readonly onDidAddContainer: Event; - readonly onDidContainersChange: Event; - readonly activeContainer: ViewContainer | undefined; - addContainer(container: ViewContainer): void; - setActiveViewContainer(id: string): void; - getView(id: string): View | undefined; - moveViewToLocation( - view: View, - targetViewContainer: ViewContainer, - targetLocation: number - ): void; - insertContainerAfter(source: ViewContainer, target: ViewContainer): void; - insertContainerBefore(source: ViewContainer, target: ViewContainer): void; - addViews(view: View, viewContainer: ViewContainer, location?: number): void; - removeViews(removeViews: View[], viewContainer: ViewContainer): void; - getViewContainer(id: string): ViewContainer | undefined; - getViewContainer2(view: View): ViewContainer | undefined; - toJSON(): SerializedViewService; - load(layout: SerializedViewService): void; -} - -export class ViewService implements IViewService { - private _viewContainers: ViewContainer[] = []; - private readonly _onDidActiveContainerChange = new Emitter(); - readonly onDidActiveContainerChange = this._onDidActiveContainerChange - .event; - private readonly _onDidRemoveContainer = new Emitter(); - readonly onDidRemoveContainer = this._onDidRemoveContainer.event; - private readonly _onDidAddContainer = new Emitter(); - readonly onDidAddContainer = this._onDidAddContainer.event; - private readonly _onDidContainersChange = new Emitter(); - readonly onDidContainersChange = this._onDidContainersChange.event; - private _activeViewContainerId: string; - - get containers(): ViewContainer[] { - return this._viewContainers; - } - - get activeContainer(): ViewContainer | undefined { - return this._viewContainers.find( - (c) => c.id === this._activeViewContainerId - ); - } - - constructor(private readonly viewRegistry: IViewRegistry) { - // - } - - load(layout: SerializedViewService): void { - const { containers, activeContainer } = layout; - - for (const container of containers) { - const { id, views } = container; - const viewContainer = new PaneviewContainer( - id, - this.viewRegistry, - views - ); - - this.addContainer(viewContainer); - } - - this.setActiveViewContainer(activeContainer); - } - - insertContainerAfter(source: ViewContainer, target: ViewContainer): void { - const sourceIndex = this._viewContainers.findIndex( - (c) => c.id === source.id - ); - - const view = this._viewContainers.splice(sourceIndex, 1)[0]; - - const targetIndex = this._viewContainers.findIndex( - (c) => c.id === target.id - ); - - this._viewContainers.splice(targetIndex + 1, 0, view); - this._viewContainers = [...this._viewContainers]; - - this._onDidContainersChange.fire(); - } - - insertContainerBefore(source: ViewContainer, target: ViewContainer): void { - const sourceIndex = this._viewContainers.findIndex( - (c) => c.id === source.id - ); - - const view = this._viewContainers.splice(sourceIndex, 1)[0]; - - const targetIndex = this._viewContainers.findIndex( - (c) => c.id === target.id - ); - - this._viewContainers.splice(Math.max(targetIndex, 0), 0, view); - this._viewContainers = [...this._viewContainers]; - - this._onDidContainersChange.fire(); - } - - addContainer(container: ViewContainer): void { - this._viewContainers = [...this._viewContainers, container]; - this._activeViewContainerId = container.id; - this._onDidAddContainer.fire(); - } - - removeContainer(container: ViewContainer): void { - this._viewContainers = this._viewContainers.filter( - (c) => c.id !== container.id - ); - - if (this._activeViewContainerId === container.id) { - this._activeViewContainerId = - this._viewContainers.length > 0 - ? this._viewContainers[0].id - : undefined; - } - - this._onDidRemoveContainer.fire(); - } - - setActiveViewContainer(id: string): void { - if (!this._viewContainers.find((c) => c.id === id)) { - throw new Error(`invalid container ${id}`); - } - this._activeViewContainerId = id; - this._onDidActiveContainerChange.fire(); - } - - getView(id: string): View | undefined { - for (const container of Array.from(this._viewContainers.values())) { - const view = container.views.find((v) => v.id === id); - if (view) { - return view; - } - } - return undefined; - } - - moveViewToLocation( - view: View, - targetViewContainer: ViewContainer, - targetLocation: number - ): void { - const sourceViewContainer = this.getViewContainer2(view); - this.removeViews([view], sourceViewContainer); - this.addViews(view, targetViewContainer, targetLocation); - } - - addViews( - view: View, - viewContainer: ViewContainer, - location?: number - ): void { - viewContainer.addView(view, location); - } - - removeViews(removeViews: View[], viewContainer: ViewContainer): void { - for (const view of removeViews) { - viewContainer.removeView(view); - - if (viewContainer.views.length === 0) { - viewContainer.clear(); - this.removeContainer(viewContainer); - } - } - } - - getViewContainer(id: string): ViewContainer | undefined { - return this._viewContainers.find((c) => c.id === id); - } - - getViewContainer2(view: View): ViewContainer | undefined { - for (const container of Array.from(this._viewContainers.values())) { - const v = container.views.find((v) => v.id === view.id); - if (v) { - return container; - } - } - return undefined; - } - - toJSON(): SerializedViewService { - return { - containers: this.containers.map((c) => c.toJSON()), - activeContainer: this.activeContainer.id, - }; - } - - dispose(): void { - this._onDidActiveContainerChange.dispose(); - this._onDidAddContainer.dispose(); - this._onDidRemoveContainer.dispose(); - } -} diff --git a/packages/dockview-demo/src/services/widgets.scss b/packages/dockview-demo/src/services/widgets.scss deleted file mode 100644 index 69c4abbf7..000000000 --- a/packages/dockview-demo/src/services/widgets.scss +++ /dev/null @@ -1,32 +0,0 @@ -.sidebar-part { - background-color: #252526; -} - -.activity-bar-part { - background-color: #333333; - - .activity-bar-item { - display: flex; - cursor: pointer; - justify-content: center; - align-items: center; - height: 48px; - box-sizing: border-box; - position: relative; - color: grey; - font-size: 24px; - - &:hover, - &.active { - color: white; - } - } - - .activity-bar-space { - height: 100%; - - &.activity-bar-space-dragover { - border-top: 2px solid white; - } - } -} diff --git a/packages/dockview-demo/src/services/widgets.tsx b/packages/dockview-demo/src/services/widgets.tsx deleted file mode 100644 index 555d0cb64..000000000 --- a/packages/dockview-demo/src/services/widgets.tsx +++ /dev/null @@ -1,474 +0,0 @@ -import { - getPaneData, - GridviewApi, - IGridviewPanelProps, - IPaneviewPanelProps, - PanelCollection, - PaneviewApi, - PaneviewDropEvent, - PaneviewReact, - PaneviewReadyEvent, - Position, -} from 'dockview'; -import * as React from 'react'; -import { useLayoutRegistry } from '../layout-grid/registry'; -import { PaneviewContainer, ViewContainer } from './viewContainer'; -import { IViewService, ViewService } from './viewService'; -import { DefaultView } from './view'; -import { RegisteredView, VIEW_REGISTRY } from './viewRegistry'; -import { toggleClass } from '../dom'; -import { Container } from './sidebarItem'; -import './widgets.scss'; -import { CompositeDisposable } from '../lifecycle'; - -class ViewServiceModel { - private readonly viewService: IViewService; - - get model() { - return this.viewService; - } - - constructor() { - this.viewService = new ViewService(VIEW_REGISTRY); - this.init(); - } - - init(): void { - const layout = localStorage.getItem('viewservice'); - if (layout) { - this.viewService.load(JSON.parse(layout)); - } else { - const container1 = new PaneviewContainer( - 'default_container_1', - VIEW_REGISTRY - ); - if (!container1.schema) { - this.addView( - container1, - VIEW_REGISTRY.getRegisteredView('search_widget') - ); - this.addView( - container1, - VIEW_REGISTRY.getRegisteredView('home_widget') - ); - } - const container2 = new PaneviewContainer( - 'default_container_2', - VIEW_REGISTRY - ); - if (!container2.schema) { - this.addView( - container2, - VIEW_REGISTRY.getRegisteredView('account_widget') - ); - this.addView( - container2, - VIEW_REGISTRY.getRegisteredView('settings_widget') - ); - } - this.viewService.addContainer(container1); - this.viewService.addContainer(container2); - } - - const save = () => { - localStorage.setItem( - 'viewservice', - JSON.stringify(this.viewService.toJSON()) - ); - }; - - this.viewService.onDidActiveContainerChange(save); - this.viewService.onDidRemoveContainer(save); - this.viewService.onDidAddContainer(save); - } - - private addView( - container: ViewContainer, - registedView: RegisteredView - ): void { - container.addView( - new DefaultView({ - id: registedView.id, - title: registedView.title, - isExpanded: true, - isLocationEditable: registedView.isLocationEditable, - icon: registedView.icon, - }) - ); - } -} - -const viewService = new ViewServiceModel(); - -const colors = { - home_widget: 'red', - account_widget: 'green', - settings_widget: 'yellow', - search_widget: 'orange', -}; - -const components: PanelCollection> = { - default: (props: IPaneviewPanelProps<{ viewId: string }>) => { - const Component = React.useMemo(() => { - const registeredView = VIEW_REGISTRY.getRegisteredView( - props.params.viewId - ); - return registeredView?.component; - }, [props.params.viewId]); - - return Component ? : null; - }, -}; - -export const Activitybar = (props: IGridviewPanelProps) => { - const [activeContainerid, setActiveContainerId] = React.useState( - viewService.model.activeContainer.id - ); - const [containers, setContainers] = React.useState( - viewService.model.containers - ); - - const registry = useLayoutRegistry(); - - React.useEffect(() => { - const disposable = new CompositeDisposable( - viewService.model.onDidActiveContainerChange(() => { - setActiveContainerId(viewService.model.activeContainer.id); - }), - viewService.model.onDidAddContainer(() => { - setContainers(viewService.model.containers); - }), - viewService.model.onDidRemoveContainer(() => { - setContainers(viewService.model.containers); - }), - viewService.model.onDidContainersChange(() => { - setContainers(viewService.model.containers); - }) - ); - - return () => { - disposable.dispose(); - }; - }, []); - - const onClick = - (container: ViewContainer, alwaysOpen = false) => - (event: React.MouseEvent) => { - const api = registry.get('gridview'); - - const selectedActive = container.id === activeContainerid; - - const sidebarPanel = api.getPanel('sidebar'); - if (sidebarPanel.api.isVisible) { - if (!alwaysOpen && selectedActive) { - sidebarPanel.api.setVisible(false); - } - } else { - event.preventDefault(); // prevent focus - sidebarPanel.api.setVisible(true); - sidebarPanel.focus(); - } - - viewService.model.setActiveViewContainer(container.id); - }; - - const onContainerDrop = - (targetContainer: ViewContainer) => - (event: React.DragEvent, direction: 'top' | 'bottom') => { - const data = event.dataTransfer.getData('application/json'); - if (data) { - const { container } = JSON.parse(data); - const sourceContainer = - viewService.model.getViewContainer(container); - - switch (direction) { - case 'bottom': - viewService.model.insertContainerAfter( - sourceContainer, - targetContainer - ); - break; - case 'top': - viewService.model.insertContainerBefore( - sourceContainer, - targetContainer - ); - break; - } - - viewService.model.setActiveViewContainer(sourceContainer.id); - } - }; - - const onNewContainer = (event: React.DragEvent) => { - const data = getPaneData(); - if (data) { - const { paneId } = data; - const view = viewService.model.getView(paneId); - - if (!view) { - console.log(`view ${paneId} doesn't exist`); - return; - } - - const viewContainer = viewService.model.getViewContainer2(view); - - if (!viewContainer) { - console.log(`viewContainer for view ${view.id} doesn't exist`); - return; - } - - viewService.model.removeViews([view], viewContainer); - - const newContainer = new PaneviewContainer( - `t_${Date.now().toString().substr(5)}`, - VIEW_REGISTRY - ); - newContainer.addView(view); - viewService.model.addContainer(newContainer); - } - }; - - const onDragOver = (container: ViewContainer) => (e: React.DragEvent) => { - const api = registry.get('gridview'); - - const sidebarPanel = api.getPanel('sidebar'); - if (!sidebarPanel.api.isVisible) { - sidebarPanel.api.setVisible(true); - sidebarPanel.focus(); - } - - viewService.model.setActiveViewContainer(container.id); - }; - - return ( -
- {containers.map((container, i) => { - const isActive = activeContainerid === container.id; - return ( - - ); - })} - -
- ); -}; - -const ExtraSpace = (props: { - onNewContainer: (event: React.DragEvent) => void; -}) => { - const ref = React.useRef(null); - - const onDrop = (event: React.DragEvent) => { - toggleClass(ref.current, 'activity-bar-space-dragover', false); - props.onNewContainer(event); - }; - - return ( -
{ - e.preventDefault(); - }} - onDragEnter={(e) => { - toggleClass(ref.current, 'activity-bar-space-dragover', true); - e.preventDefault(); - }} - onDragLeave={(e) => { - toggleClass(ref.current, 'activity-bar-space-dragover', false); - }} - onDrop={onDrop} - >
- ); -}; - -export const Sidebar = () => { - const [sidebarId, setSidebarId] = React.useState( - viewService.model.activeContainer.id - ); - - React.useEffect(() => { - const disposable = viewService.model.onDidActiveContainerChange(() => { - setSidebarId(viewService.model.activeContainer.id); - }); - - return () => { - disposable.dispose(); - }; - }, []); - - return ; -}; - -const headerComponents: PanelCollection = { - default: (props) => { - const onClick = () => props.api.setExpanded(!props.api.isExpanded); - return ( - - ); - }, -}; - -export const SidebarPart = (props: { id: string }) => { - const [api, setApi] = React.useState(); - - React.useEffect(() => { - if (!api) { - return () => { - // - }; - } - - const viewContainer = viewService.model.getViewContainer(props.id); - - const disposables = new CompositeDisposable( - api.onDidLayoutChange(() => { - viewContainer.layout(api.toJSON()); - }), - viewContainer.onDidAddView(({ view, index }) => { - api.addPanel({ - id: view.id, - isExpanded: view.isExpanded, - title: view.title, - component: 'default', - headerComponent: 'default', - params: { - viewId: view.id, - }, - index, - }); - }), - viewContainer.onDidRemoveView((view) => { - const panel = api.getPanel(view.id); - api.removePanel(panel); - }) - ); - - const schema = viewContainer.schema; - if (schema) { - api.fromJSON(schema); - } else { - api.panels.forEach((p) => { - api.removePanel(p); - }); - viewContainer.views.forEach((view) => { - api.addPanel({ - id: view.id, - isExpanded: view.isExpanded, - title: view.title, - component: 'default', - headerComponent: 'default', - params: { - viewId: view.id, - }, - }); - }); - } - - return () => { - disposables.dispose(); - }; - }, [api, props.id]); - - const onReady = (event: PaneviewReadyEvent) => { - setApi(event.api); - }; - - const onDidDrop = (event: PaneviewDropEvent) => { - const data = event.getData(); - - const containerData = - event.nativeEvent.dataTransfer.getData('application/json'); - - if (containerData) { - const { container } = JSON.parse(containerData); - - if (container === props.id) { - return; - } - - const sourceContainer = - viewService.model.getViewContainer(container); - const targetContainer = viewService.model.getViewContainer( - props.id - ); - - if (!sourceContainer) { - console.log(`sourceContainer ${props.id} doesn't exist`); - return; - } - if (!targetContainer) { - console.log(`targetContainer ${props.id} doesn't exist`); - return; - } - - sourceContainer.views.forEach((v) => { - viewService.model.moveViewToLocation(v, targetContainer, 0); - }); - - return; - } - - if (!data) { - return; - } - - const targetPanel = event.panel; - const allPanels = event.api.panels; - let toIndex = allPanels.indexOf(targetPanel); - - if ( - event.position === Position.Right || - event.position === Position.Bottom - ) { - toIndex = Math.min(allPanels.length, toIndex + 1); - } - - const viewId = data.paneId; - const viewContainer = viewService.model.getViewContainer(props.id); - - if (!viewContainer) { - console.log(`viewContainer ${props.id} doesn't exist`); - return; - } - - const view = viewService.model.getView(viewId); - - viewService.model.moveViewToLocation(view, viewContainer, toIndex); - }; - - if (!props.id) { - return null; - } - - return ( - - ); -}; diff --git a/packages/dockview-demo/src/stories/Introduction.stories.mdx b/packages/dockview-demo/src/stories/Introduction.stories.mdx deleted file mode 100644 index c97b50845..000000000 --- a/packages/dockview-demo/src/stories/Introduction.stories.mdx +++ /dev/null @@ -1,64 +0,0 @@ -import { Meta } from '@storybook/addon-docs/blocks'; -import Code from './assets/code-brackets.svg'; -import Colors from './assets/colors.svg'; -import Comments from './assets/comments.svg'; -import Direction from './assets/direction.svg'; -import Flow from './assets/flow.svg'; -import Plugin from './assets/plugin.svg'; -import Repo from './assets/repo.svg'; -import StackAlt from './assets/stackalt.svg'; -import Splitview from '../resources/splitview-logo.svg'; -import Gridview from '../resources/gridview-logo.svg'; -import Dockview from '../resources/dockview-logo.svg'; -import Paneview from '../resources/paneview-logo.svg'; -import './introduction.css'; - - - -# Dockview - -
Components
- - diff --git a/packages/dockview-demo/src/stories/assets/code-brackets.svg b/packages/dockview-demo/src/stories/assets/code-brackets.svg deleted file mode 100644 index 73de94776..000000000 --- a/packages/dockview-demo/src/stories/assets/code-brackets.svg +++ /dev/null @@ -1 +0,0 @@ -illustration/code-brackets \ No newline at end of file diff --git a/packages/dockview-demo/src/stories/assets/colors.svg b/packages/dockview-demo/src/stories/assets/colors.svg deleted file mode 100644 index 17d58d516..000000000 --- a/packages/dockview-demo/src/stories/assets/colors.svg +++ /dev/null @@ -1 +0,0 @@ -illustration/colors \ No newline at end of file diff --git a/packages/dockview-demo/src/stories/assets/comments.svg b/packages/dockview-demo/src/stories/assets/comments.svg deleted file mode 100644 index 6493a139f..000000000 --- a/packages/dockview-demo/src/stories/assets/comments.svg +++ /dev/null @@ -1 +0,0 @@ -illustration/comments \ No newline at end of file diff --git a/packages/dockview-demo/src/stories/assets/direction.svg b/packages/dockview-demo/src/stories/assets/direction.svg deleted file mode 100644 index 65676ac27..000000000 --- a/packages/dockview-demo/src/stories/assets/direction.svg +++ /dev/null @@ -1 +0,0 @@ -illustration/direction \ No newline at end of file diff --git a/packages/dockview-demo/src/stories/assets/flow.svg b/packages/dockview-demo/src/stories/assets/flow.svg deleted file mode 100644 index 8ac27db40..000000000 --- a/packages/dockview-demo/src/stories/assets/flow.svg +++ /dev/null @@ -1 +0,0 @@ -illustration/flow \ No newline at end of file diff --git a/packages/dockview-demo/src/stories/assets/plugin.svg b/packages/dockview-demo/src/stories/assets/plugin.svg deleted file mode 100644 index 29e5c690c..000000000 --- a/packages/dockview-demo/src/stories/assets/plugin.svg +++ /dev/null @@ -1 +0,0 @@ -illustration/plugin \ No newline at end of file diff --git a/packages/dockview-demo/src/stories/assets/repo.svg b/packages/dockview-demo/src/stories/assets/repo.svg deleted file mode 100644 index f386ee902..000000000 --- a/packages/dockview-demo/src/stories/assets/repo.svg +++ /dev/null @@ -1 +0,0 @@ -illustration/repo \ No newline at end of file diff --git a/packages/dockview-demo/src/stories/assets/stackalt.svg b/packages/dockview-demo/src/stories/assets/stackalt.svg deleted file mode 100644 index 9b7ad2743..000000000 --- a/packages/dockview-demo/src/stories/assets/stackalt.svg +++ /dev/null @@ -1 +0,0 @@ -illustration/stackalt \ No newline at end of file diff --git a/packages/dockview-demo/src/stories/dockview/dockview.documentation.stories.mdx b/packages/dockview-demo/src/stories/dockview/dockview.documentation.stories.mdx deleted file mode 100644 index 94cded4f6..000000000 --- a/packages/dockview-demo/src/stories/dockview/dockview.documentation.stories.mdx +++ /dev/null @@ -1,7 +0,0 @@ -import { Meta } from '@storybook/addon-docs/blocks'; - - - -# Dockview documentation - -
Components
diff --git a/packages/dockview-demo/src/stories/dockview/dockview.iframe.stories.tsx b/packages/dockview-demo/src/stories/dockview/dockview.iframe.stories.tsx deleted file mode 100644 index 88a53a716..000000000 --- a/packages/dockview-demo/src/stories/dockview/dockview.iframe.stories.tsx +++ /dev/null @@ -1,108 +0,0 @@ -import { - DockviewApi, - DockviewReact, - DockviewReadyEvent, - IDockviewPanelProps, - PanelCollection, -} from 'dockview'; -import * as React from 'react'; -import { Meta } from '@storybook/react'; - -const components: PanelCollection = { - default: (props) => { - return ( -
- This content is not within an IFrame -
- ); - }, - iframe: (props) => { - return ( -
-
- The contents below is within an iFrame -
- -
- ); - }, -}; - -export const Params = (props: { - theme: string; - hideBorders: boolean; - disableAutoResizing: boolean; -}) => { - const api = React.useRef(); - - React.useEffect(() => { - if (!api.current) { - return () => { - // noop - }; - } - - const gridApi = api.current; - - const interval = setInterval(() => { - const panel1 = gridApi.getPanel('panel1'); - const panel2 = gridApi.getPanel('panel2'); - - panel1.update({ params: { params: { ticker: Date.now() } } }); - - panel2.api.setTitle(`Panel2 ${Date.now()}`); - }, 1000); - return () => { - clearInterval(interval); - }; - }, [api]); - - const onReady = (event: DockviewReadyEvent) => { - api.current = event.api; - - event.api.addPanel({ - id: 'panel1', - component: 'ticker', - params: { - ticker: 0, - }, - }); - event.api.addPanel({ - id: 'panel2', - component: 'default', - }); - event.api.addPanel({ - id: 'panel3', - component: 'default', - position: { referencePanel: 'panel1', direction: 'right' }, - }); - event.api.addPanel({ - id: 'panel4', - component: 'default', - position: { referencePanel: 'panel3', direction: 'below' }, - }); - - // event.api.getPanel('panel1').api; - }; - - return ( - - ); -}; - -export default { - title: 'Library/Dockview/Params', - component: Params, - decorators: [ - (Component) => { - document.body.style.padding = '0px'; - return ( -
- -
- ); - }, - ], - args: { theme: 'dockview-theme-light' }, - argTypes: { - theme: { - control: { - type: 'select', - options: ['dockview-theme-dark', 'dockview-theme-light'], - }, - }, - }, -} as Meta; diff --git a/packages/dockview-demo/src/stories/dockview/dockview.simple.stories.tsx b/packages/dockview-demo/src/stories/dockview/dockview.simple.stories.tsx deleted file mode 100644 index d6a9e93b4..000000000 --- a/packages/dockview-demo/src/stories/dockview/dockview.simple.stories.tsx +++ /dev/null @@ -1,93 +0,0 @@ -import { - DockviewApi, - DockviewReact, - DockviewReadyEvent, - IDockviewPanelProps, - PanelCollection, -} from 'dockview'; -import * as React from 'react'; -import { Meta } from '@storybook/react'; - -const components: PanelCollection = { - default: (props) => { - return ( -
hello world
- ); - }, - iframe: (props) => { - return ( -
- -
- ); - }, -}; - -export const Simple = (props: { - theme: string; - hideBorders: boolean; - disableAutoResizing: boolean; -}) => { - const api = React.useRef(); - - const onReady = (event: DockviewReadyEvent) => { - api.current = event.api; - - event.api.addPanel({ - id: 'panel1', - component: 'default', - }); - event.api.addPanel({ - id: 'panel2', - component: 'default', - }); - event.api.addPanel({ - id: 'panel3', - component: 'default', - position: { referencePanel: 'panel1', direction: 'right' }, - }); - event.api.addPanel({ - id: 'panel4', - component: 'default', - position: { referencePanel: 'panel3', direction: 'below' }, - }); - - // event.api.getPanel('panel1').api; - }; - - return ( - - ); -}; - -export default { - title: 'Library/Dockview/Simple', - component: Simple, - decorators: [ - (Component) => { - document.body.style.padding = '0px'; - return ( -
- -
- ); - }, - ], - args: { theme: 'dockview-theme-light' }, - argTypes: { - theme: { - control: { - type: 'select', - options: ['dockview-theme-dark', 'dockview-theme-light'], - }, - }, - }, -} as Meta; diff --git a/packages/dockview-demo/src/stories/dockview/dockview.tab.stories.tsx b/packages/dockview-demo/src/stories/dockview/dockview.tab.stories.tsx deleted file mode 100644 index 9f5edfc5e..000000000 --- a/packages/dockview-demo/src/stories/dockview/dockview.tab.stories.tsx +++ /dev/null @@ -1,85 +0,0 @@ -import { - DockviewApi, - DockviewReact, - DockviewReadyEvent, - IDockviewPanelProps, - PanelCollection, -} from 'dockview'; -import * as React from 'react'; -import { Meta } from '@storybook/react'; - -const components: PanelCollection = { - default: (props) => { - const close = () => props.api.close(); - return ( -
hello world
- ); - }, -}; - -export const Tab = (props: { - theme: string; - hideBorders: boolean; - disableAutoResizing: boolean; -}) => { - const api = React.useRef(); - - const onReady = (event: DockviewReadyEvent) => { - api.current = event.api; - - event.api.addPanel({ - id: 'panel1', - component: 'default', - }); - event.api.addPanel({ - id: 'panel2', - component: 'default', - }); - event.api.addPanel({ - id: 'panel3', - component: 'default', - position: { referencePanel: 'panel1', direction: 'right' }, - }); - event.api.addPanel({ - id: 'panel4', - component: 'default', - position: { referencePanel: 'panel3', direction: 'below' }, - }); - - // event.api.getPanel('panel1').api; - }; - - return ( - - ); -}; - -export default { - title: 'Library/Dockview/Tab', - component: Tab, - decorators: [ - (Component) => { - document.body.style.padding = '0px'; - return ( -
- -
- ); - }, - ], - args: { theme: 'dockview-theme-light' }, - argTypes: { - theme: { - control: { - type: 'select', - options: ['dockview-theme-dark', 'dockview-theme-light'], - }, - }, - }, -} as Meta; diff --git a/packages/dockview-demo/src/stories/dockview/dockview.watermark.stories.tsx b/packages/dockview-demo/src/stories/dockview/dockview.watermark.stories.tsx deleted file mode 100644 index 49701d32e..000000000 --- a/packages/dockview-demo/src/stories/dockview/dockview.watermark.stories.tsx +++ /dev/null @@ -1,145 +0,0 @@ -import { - DockviewApi, - DockviewReact, - DockviewReadyEvent, - IDockviewPanelProps, - IWatermarkPanelProps, - PanelCollection, -} from 'dockview'; -import { CompositeDisposable } from '../../lifecycle'; -import * as React from 'react'; -import { Meta } from '@storybook/react'; - -const components: PanelCollection = { - default: (props) => { - return ( -
hello world
- ); - }, - iframe: (props) => { - return ( -
- -
- ); - }, -}; - -const WatermarkPanel = (props: IWatermarkPanelProps) => { - const [size, setSize] = React.useState(props.containerApi.size); - const [panels, setPanels] = React.useState( - props.containerApi.totalPanels - ); - - React.useEffect(() => { - const disposables = new CompositeDisposable( - props.containerApi.onDidAddGroup((event) => { - setSize(props.containerApi.size); - }), - props.containerApi.onDidRemoveGroup((event) => { - setSize(props.containerApi.size); - }), - props.containerApi.onDidAddPanel((event) => { - setPanels(props.containerApi.totalPanels); - }), - props.containerApi.onDidRemovePanel((event) => { - setPanels(props.containerApi.totalPanels); - }) - ); - - return () => { - disposables.dispose(); - }; - }, []); - - const onClick = () => { - props.containerApi.addPanel({ - id: Date.now().toString(), - component: 'default', - position: { direction: 'right' }, - }); - }; - const onReplace = () => { - props.containerApi.addPanel({ - id: Date.now().toString(), - component: 'default', - }); - }; - - const onAddEmptyGroup = () => { - props.containerApi.addEmptyGroup(); - }; - - return ( -
-
    -
  • - This is a custom watermark for when a group has no panels to - display. -
  • -
  • - The watermark is only automatically shown when there are no - panels to display. You can otherwise add a watermark - programatically. -
  • -
-
{`Total groups: ${size}`}
-
{`Total panels: ${panels}`}
- - - - {size > 1 && } -
- ); -}; - -export const Watermark = (props: { - theme: string; - hideBorders: boolean; - disableAutoResizing: boolean; -}) => { - const api = React.useRef(); - - const onReady = (event: DockviewReadyEvent) => { - api.current = event.api; - - event.api.addEmptyGroup(); - }; - - return ( - - ); -}; - -export default { - title: 'Library/Dockview/Watermark', - component: Watermark, - decorators: [ - (Component) => { - document.body.style.padding = '0px'; - return ( -
- -
- ); - }, - ], - args: { theme: 'dockview-theme-light' }, - argTypes: { - theme: { - control: { - type: 'select', - options: ['dockview-theme-dark', 'dockview-theme-light'], - }, - }, - }, -} as Meta; diff --git a/packages/dockview-demo/src/stories/gridview/gridview.documentation.stories.mdx b/packages/dockview-demo/src/stories/gridview/gridview.documentation.stories.mdx deleted file mode 100644 index 790c78267..000000000 --- a/packages/dockview-demo/src/stories/gridview/gridview.documentation.stories.mdx +++ /dev/null @@ -1,7 +0,0 @@ -import { Meta } from '@storybook/addon-docs/blocks'; - - - -# Gridview documentation - -
Components
diff --git a/packages/dockview-demo/src/stories/gridview/gridview.params.stories.tsx b/packages/dockview-demo/src/stories/gridview/gridview.params.stories.tsx deleted file mode 100644 index 7e5a2259b..000000000 --- a/packages/dockview-demo/src/stories/gridview/gridview.params.stories.tsx +++ /dev/null @@ -1,107 +0,0 @@ -import { - GridviewApi, - GridviewReact, - GridviewReadyEvent, - IGridviewPanelProps, - Orientation, - PanelCollection, -} from 'dockview'; -import * as React from 'react'; -import { Meta } from '@storybook/react'; - -const components: PanelCollection> = { - default: (props) => { - return ( -
hello world
- ); - }, - ticker: (props: IGridviewPanelProps<{ ticker: number }>) => { - return ( -
- {`The current ticker value is ${props.params.ticker}`} -
- ); - }, -}; - -export const Params = (props: { - theme: string; - hideBorders: boolean; - disableAutoResizing: boolean; -}) => { - const api = React.useRef(); - - React.useEffect(() => { - if (!api.current) { - return () => { - // noop - }; - } - - const gridApi = api.current; - - const interval = setInterval(() => { - const panel1 = gridApi.getPanel('panel1'); - - panel1.update({ params: { ticker: Date.now() } }); - }, 1000); - return () => { - clearInterval(interval); - }; - }, [api]); - - const onReady = (event: GridviewReadyEvent) => { - api.current = event.api; - - event.api.addPanel({ - id: 'panel1', - component: 'ticker', - params: { - ticker: 0, - }, - }); - event.api.addPanel({ - id: 'panel2', - component: 'default', - }); - event.api.addPanel({ - id: 'panel3', - component: 'default', - }); - }; - - return ( - - ); -}; - -export default { - title: 'Library/Gridview/Params', - component: Params, - decorators: [ - (Component) => { - document.body.style.padding = '0px'; - return ( -
- -
- ); - }, - ], - args: { theme: 'dockview-theme-light' }, - argTypes: { - theme: { - control: { - type: 'select', - options: ['dockview-theme-dark', 'dockview-theme-light'], - }, - }, - }, -} as Meta; diff --git a/packages/dockview-demo/src/stories/gridview/gridview.simple.stories.tsx b/packages/dockview-demo/src/stories/gridview/gridview.simple.stories.tsx deleted file mode 100644 index dbfdbf812..000000000 --- a/packages/dockview-demo/src/stories/gridview/gridview.simple.stories.tsx +++ /dev/null @@ -1,137 +0,0 @@ -import { - GridviewApi, - GridviewReact, - GridviewReadyEvent, - IGridviewPanelProps, - Orientation, - orthogonal, - PanelCollection, -} from 'dockview'; -import * as React from 'react'; -import { Meta } from '@storybook/react'; - -const components: PanelCollection> = { - default: (props: IGridviewPanelProps<{ color: string }>) => { - const transpose = () => { - props.containerApi.orientation = orthogonal( - props.containerApi.orientation - ); - }; - - const resizeWidth = () => { - props.api.setSize({ width: 300 }); - }; - - const resizeHeight = () => { - props.api.setSize({ height: 300 }); - }; - - return ( -
-
{'hello world'}
- - - -
- ); - }, -}; - -export const Simple = (props: { - orientation: Orientation; - hideBorders: boolean; - proportionalLayout: boolean; - disableAutoResizing: boolean; -}) => { - const api = React.useRef(); - - const onReady = (event: GridviewReadyEvent) => { - api.current = event.api; - - event.api.addPanel({ - id: 'panel_1', - component: 'default', - params: { color: 'red' }, - minimumHeight: 50, - minimumWidth: 50, - location: [0], - }); - event.api.addPanel({ - id: 'panel_2', - component: 'default', - params: { color: 'green' }, - minimumHeight: 50, - minimumWidth: 50, - location: [0, 0], - }); - event.api.addPanel({ - id: 'panel_3', - component: 'default', - params: { color: 'purple' }, - minimumHeight: 50, - minimumWidth: 50, - location: [0, 0, 0], - }); - event.api.addPanel({ - id: 'panel_4', - component: 'default', - params: { color: 'yellow' }, - minimumHeight: 50, - minimumWidth: 50, - location: [0, 0, 0, 0], - }); - event.api.addPanel({ - id: 'panel_5', - component: 'default', - params: { color: 'dodgerblue' }, - minimumHeight: 50, - minimumWidth: 50, - location: [0, 0, 0, 0, 0], - }); - }; - - return ( - - ); -}; - -export default { - title: 'Library/Gridview/Simple', - component: Simple, - decorators: [ - (Component) => { - document.body.style.padding = '0px'; - return ( -
- -
- ); - }, - ], - args: { - orientation: Orientation.VERTICAL, - proportionalLayout: true, - disableAutoResizing: false, - }, - argTypes: { - orientation: { - control: { - type: 'inline-radio', - options: [Orientation.HORIZONTAL, Orientation.VERTICAL], - }, - }, - }, -} as Meta; diff --git a/packages/dockview-demo/src/stories/introduction.css b/packages/dockview-demo/src/stories/introduction.css deleted file mode 100644 index 128e02003..000000000 --- a/packages/dockview-demo/src/stories/introduction.css +++ /dev/null @@ -1,100 +0,0 @@ -.subheading { - --mediumdark: '#999999'; - font-weight: 900; - font-size: 13px; - color: #999; - letter-spacing: 6px; - line-height: 24px; - text-transform: uppercase; - margin-bottom: 12px; - margin-top: 40px; -} - -.link-list { - display: grid; - grid-template-columns: 1fr; - grid-template-rows: 1fr 1fr; - row-gap: 10px; -} - -@media (min-width: 620px) { - .link-list { - row-gap: 20px; - column-gap: 20px; - grid-template-columns: 1fr 1fr; - } -} - -@media all and (-ms-high-contrast: none) { - .link-list { - display: -ms-grid; - -ms-grid-columns: 1fr 1fr; - -ms-grid-rows: 1fr 1fr; - } -} - -.link-item { - display: block; - padding: 20px 30px 20px 15px; - border: 1px solid #00000010; - border-radius: 5px; - transition: background 150ms ease-out, border 150ms ease-out, - transform 150ms ease-out; - color: #333333; - display: flex; - align-items: flex-start; -} - -.link-item:hover { - border-color: #1ea7fd50; - transform: translate3d(0, -3px, 0); - box-shadow: rgba(0, 0, 0, 0.08) 0 3px 10px 0; -} - -.link-item:active { - border-color: #1ea7fd; - transform: translate3d(0, 0, 0); -} - -.link-item strong { - font-weight: 700; - display: block; - margin-bottom: 2px; -} - -.link-item img { - height: 40px; - width: 40px; - margin-right: 15px; - flex: none; -} - -.link-item span { - font-size: 14px; - line-height: 20px; -} - -.tip { - display: inline-block; - border-radius: 1em; - font-size: 11px; - line-height: 12px; - font-weight: 700; - background: #e7fdd8; - color: #66bf3c; - padding: 4px 12px; - margin-right: 10px; - vertical-align: top; -} - -.tip-wrapper { - font-size: 13px; - line-height: 20px; - margin-top: 40px; - margin-bottom: 40px; -} - -.tip-wrapper code { - font-size: 12px; - display: inline-block; -} diff --git a/packages/dockview-demo/src/stories/paneview/paneview.deserialization.stories.tsx b/packages/dockview-demo/src/stories/paneview/paneview.deserialization.stories.tsx deleted file mode 100644 index c5ac57df8..000000000 --- a/packages/dockview-demo/src/stories/paneview/paneview.deserialization.stories.tsx +++ /dev/null @@ -1,161 +0,0 @@ -import { - PanelCollection, - PaneviewReact, - PaneviewApi, - PaneviewReadyEvent, - IPaneviewPanelProps, - PanelConstraintChangeEvent, - PanelDimensionChangeEvent, - ExpansionEvent, - FocusEvent, - ActiveEvent, -} from 'dockview'; -import * as React from 'react'; -import { Meta } from '@storybook/react'; -import { CompositeDisposable } from '../../lifecycle'; - -const components: PanelCollection> = { - default: (props: IPaneviewPanelProps<{ color: string }>) => { - const [ - constraints, - setConstraints, - ] = React.useState(); - const [ - dimensions, - setDimensions, - ] = React.useState(); - const [ - expansionState, - setExpansionState, - ] = React.useState(); - const [active, setActive] = React.useState(); - const [focus, setFocus] = React.useState(); - - React.useEffect(() => { - const disposables = new CompositeDisposable( - props.api.onDidConstraintsChange(setConstraints), - props.api.onDidDimensionsChange(setDimensions), - props.api.onDidExpansionChange(setExpansionState), - props.api.onDidActiveChange(setActive), - props.api.onDidFocusChange(setFocus) - ); - - return () => { - disposables.dispose(); - }; - }, []); - - const resize = () => { - props.api.setSize({ size: 300 }); - }; - - return ( -
-
-
Contraints:
-
{`maximumSize: ${constraints?.maximumSize} minimumSize: ${constraints?.minimumSize}`}
-
Dimesions:
-
{`width: ${dimensions?.width} height: ${dimensions?.height}`}
-
Expansion:
-
{`expanded: ${expansionState?.isExpanded}`}
-
Active:
-
{`active: ${active?.isActive}`}
-
Focus:
-
{`focused: ${focus?.isFocused}`}
-
- -
- ); - }, -}; - -export const Deserialization = (props: { - theme: string; - disableAutoResizing: boolean; -}) => { - const api = React.useRef(); - - const onReady = (event: PaneviewReadyEvent) => { - api.current = event.api; - - event.api.fromJSON({ - size: 100, - views: [ - { - size: 80, - expanded: true, - minimumSize: 100, - data: { - id: 'panel1', - component: 'default', - title: 'Panel 1', - }, - }, - { - size: 20, - expanded: true, - minimumSize: 100, - data: { - id: 'panel2', - component: 'default', - title: 'Panel 2', - }, - }, - { - size: 20, - expanded: false, - minimumSize: 100, - data: { - id: 'panel3', - component: 'default', - title: 'Panel 3', - }, - }, - ], - }); - - event.api.getPanel('panel2')?.api.setSize({ size: 60 }); - }; - - return ( - - ); -}; - -export default { - title: 'Library/Paneview/Deserialization', - component: Deserialization, - decorators: [ - (Component) => { - document.body.style.padding = '0px'; - return ( -
- -
- ); - }, - ], - args: { theme: 'dockview-theme-light', disableAutoResizing: false }, - argTypes: { - theme: { - control: { - type: 'select', - options: ['dockview-theme-dark', 'dockview-theme-light'], - }, - }, - }, -} as Meta; diff --git a/packages/dockview-demo/src/stories/paneview/paneview.documentation.stories.mdx b/packages/dockview-demo/src/stories/paneview/paneview.documentation.stories.mdx deleted file mode 100644 index f99caba3a..000000000 --- a/packages/dockview-demo/src/stories/paneview/paneview.documentation.stories.mdx +++ /dev/null @@ -1,7 +0,0 @@ -import { Meta } from '@storybook/addon-docs/blocks'; - - - -# Paneview documentation - -
Components
diff --git a/packages/dockview-demo/src/stories/paneview/paneview.persistance.stories.tsx b/packages/dockview-demo/src/stories/paneview/paneview.persistance.stories.tsx deleted file mode 100644 index 73fd07bfb..000000000 --- a/packages/dockview-demo/src/stories/paneview/paneview.persistance.stories.tsx +++ /dev/null @@ -1,174 +0,0 @@ -import { - PanelCollection, - PaneviewReact, - PaneviewApi, - PaneviewReadyEvent, - IPaneviewPanelProps, - SerializedPaneview, - PanelConstraintChangeEvent, - PanelDimensionChangeEvent, - ExpansionEvent, - FocusEvent, - ActiveEvent, -} from 'dockview'; -import { CompositeDisposable } from '../../lifecycle'; -import * as React from 'react'; -import { Meta } from '@storybook/react'; - -const components: PanelCollection> = { - default: (props: IPaneviewPanelProps<{ color: string }>) => { - const [ - constraints, - setConstraints, - ] = React.useState(); - const [ - dimensions, - setDimensions, - ] = React.useState(); - const [ - expansionState, - setExpansionState, - ] = React.useState(); - const [active, setActive] = React.useState(); - const [focus, setFocus] = React.useState(); - - React.useEffect(() => { - const disposables = new CompositeDisposable( - props.api.onDidConstraintsChange(setConstraints), - props.api.onDidDimensionsChange(setDimensions), - props.api.onDidExpansionChange(setExpansionState), - props.api.onDidActiveChange(setActive), - props.api.onDidFocusChange(setFocus) - ); - - return () => { - disposables.dispose(); - }; - }, []); - - const resize = () => { - props.api.setSize({ size: 300 }); - }; - - return ( -
-
-
Contraints:
-
{`maximumSize: ${constraints?.maximumSize} minimumSize: ${constraints?.minimumSize}`}
-
Dimesions:
-
{`width: ${dimensions?.width} height: ${dimensions?.height}`}
-
Expansion:
-
{`expanded: ${expansionState?.isExpanded}`}
-
Active:
-
{`active: ${active?.isActive}`}
-
Focus:
-
{`focused: ${focus?.isFocused}`}
-
- -
- ); - }, -}; - -export const Persistance = (props: { - theme: string; - disableAutoResizing: boolean; -}) => { - const api = React.useRef(); - - const onReady = (event: PaneviewReadyEvent) => { - api.current = event.api; - - event.api.onDidLayoutChange(() => { - const state = event.api.toJSON(); - localStorage.setItem('paneview.test.layout', JSON.stringify(state)); - console.log(JSON.stringify(state, null, 4)); - }); - - const state = localStorage.getItem('paneview.test.layout'); - if (state) { - event.api.fromJSON(JSON.parse(state) as SerializedPaneview); - return; - } - - event.api.fromJSON({ - size: 100, - views: [ - { - size: 80, - expanded: true, - minimumSize: 100, - data: { - id: 'panel1', - component: 'default', - title: 'Panel 1', - }, - }, - { - size: 20, - expanded: true, - minimumSize: 100, - data: { - id: 'panel2', - component: 'default', - title: 'Panel 2', - }, - }, - { - size: 20, - expanded: false, - minimumSize: 100, - data: { - id: 'panel3', - component: 'default', - title: 'Panel 3', - }, - }, - ], - }); - - event.api.getPanel('panel2')?.api.setSize({ size: 60 }); - }; - - return ( - - ); -}; - -export default { - title: 'Library/Paneview/Persistance', - component: Persistance, - decorators: [ - (Component) => { - document.body.style.padding = '0px'; - return ( -
- -
- ); - }, - ], - args: { theme: 'dockview-theme-light', disableAutoResizing: false }, - argTypes: { - theme: { - control: { - type: 'select', - options: ['dockview-theme-dark', 'dockview-theme-light'], - }, - }, - }, -} as Meta; diff --git a/packages/dockview-demo/src/stories/paneview/paneview.simple.stories.tsx b/packages/dockview-demo/src/stories/paneview/paneview.simple.stories.tsx deleted file mode 100644 index d52f341bf..000000000 --- a/packages/dockview-demo/src/stories/paneview/paneview.simple.stories.tsx +++ /dev/null @@ -1,145 +0,0 @@ -import { - PanelCollection, - PaneviewReact, - PaneviewApi, - PaneviewReadyEvent, - IPaneviewPanelProps, - PanelConstraintChangeEvent, - PanelDimensionChangeEvent, - ExpansionEvent, - FocusEvent, - ActiveEvent, -} from 'dockview'; -import { CompositeDisposable } from '../../lifecycle'; -import * as React from 'react'; -import { Story, Meta } from '@storybook/react'; - -const components: PanelCollection> = { - default: (props: IPaneviewPanelProps<{ color: string }>) => { - const [ - constraints, - setConstraints, - ] = React.useState(); - const [ - dimensions, - setDimensions, - ] = React.useState(); - const [ - expansionState, - setExpansionState, - ] = React.useState(); - const [active, setActive] = React.useState(); - const [focus, setFocus] = React.useState(); - - React.useEffect(() => { - const disposables = new CompositeDisposable( - props.api.onDidConstraintsChange(setConstraints), - props.api.onDidDimensionsChange(setDimensions), - props.api.onDidExpansionChange(setExpansionState), - props.api.onDidActiveChange(setActive), - props.api.onDidFocusChange(setFocus) - ); - - return () => { - disposables.dispose(); - }; - }, []); - - const resize = () => { - props.api.setSize({ size: 300 }); - }; - - return ( -
-
-
Contraints:
-
{`maximumSize: ${constraints?.maximumSize} minimumSize: ${constraints?.minimumSize}`}
-
Dimesions:
-
{`width: ${dimensions?.width} height: ${dimensions?.height}`}
-
Expansion:
-
{`expanded: ${expansionState?.isExpanded}`}
-
Active:
-
{`active: ${active?.isActive}`}
-
Focus:
-
{`focused: ${focus?.isFocused}`}
-
- -
- ); - }, -}; - -export const Simple = (props: { - theme: string; - disableAutoResizing: boolean; -}) => { - const api = React.useRef(); - - const onReady = (event: PaneviewReadyEvent) => { - api.current = event.api; - - event.api.addPanel({ - id: 'panel1', - component: 'default', - params: { color: 'red' }, - title: 'Panel1', - minimumBodySize: 100, - }); - event.api.addPanel({ - id: 'panel2', - component: 'default', - params: { color: 'green' }, - title: 'Panel 2', - minimumBodySize: 100, - }); - event.api.addPanel({ - id: 'panel3', - component: 'default', - params: { color: 'purple' }, - title: 'Panel 3', - minimumBodySize: 100, - }); - }; - - return ( - - ); -}; - -export default { - title: 'Library/Paneview/Simple', - component: Simple, - decorators: [ - (Component) => { - document.body.style.padding = '0px'; - return ( -
- -
- ); - }, - ], - args: { theme: 'dockview-theme-light', disableAutoResizing: false }, - argTypes: { - theme: { - control: { - type: 'select', - options: ['dockview-theme-dark', 'dockview-theme-light'], - }, - }, - }, -} as Meta; diff --git a/packages/dockview-demo/src/stories/splitview/constraints.jpg b/packages/dockview-demo/src/stories/splitview/constraints.jpg deleted file mode 100644 index a43a30933..000000000 Binary files a/packages/dockview-demo/src/stories/splitview/constraints.jpg and /dev/null differ diff --git a/packages/dockview-demo/src/stories/splitview/splitview.css b/packages/dockview-demo/src/stories/splitview/splitview.css deleted file mode 100644 index a3f74be35..000000000 --- a/packages/dockview-demo/src/stories/splitview/splitview.css +++ /dev/null @@ -1,74 +0,0 @@ -.markdown-line { - padding-left: 20px; -} - -.indent-1 { - padding-left: 40px; -} - -.markdown-highlight { - background-color: rgba(30, 144, 255, 0.1); -} - -.sash { - background-color: orange; - position: absolute; - top: 0px; - width: 4px; - height: 100%; - z-index: 2; - cursor: ew-resize; - user-select: none; -} - -.debug-sash-max { - height: 10px; - width: 1px; - position: absolute; - z-index: 999; - top: -10px; -} - -.debug-sash-min { - height: 10px; - width: 1px; - position: absolute; - z-index: 999; - top: 100%; -} - -.debug-sash-text { - height: 20px; - line-height: 20px; - width: 80px; - display: flex; - justify-content: center; - position: absolute; - z-index: 999; - font-size: 14px; -} - -.sash-container { - position: absolute; - height: 100%; -} - -.view-container { - position: relative; - height: 100%; -} - -.view { - position: absolute; - height: 100%; - background-color: dodgerblue; - z-index: 1; - top: 0px; - padding: 10px; - color: white; - box-sizing: border-box; -} - -.sash.drag-sash { - background-color: red; -} diff --git a/packages/dockview-demo/src/stories/splitview/splitview.deserialization.stories.tsx b/packages/dockview-demo/src/stories/splitview/splitview.deserialization.stories.tsx deleted file mode 100644 index cbdb3bfb6..000000000 --- a/packages/dockview-demo/src/stories/splitview/splitview.deserialization.stories.tsx +++ /dev/null @@ -1,122 +0,0 @@ -import { - ISplitviewPanelProps, - Orientation, - PanelCollection, - SplitviewApi, - SplitviewReact, - SplitviewReadyEvent, -} from 'dockview'; -import * as React from 'react'; -import { Meta } from '@storybook/react'; - -const components: PanelCollection = { - default: (props: ISplitviewPanelProps<{ color: string }>) => { - const resize = () => { - props.api.setSize({ size: 300 }); - }; - - return ( -
-
hello world
- -
- ); - }, -}; - -export const Deserialization = (props: { - orientation: Orientation; - hideBorders: boolean; - proportionalLayout: boolean; - disableAutoResizing: boolean; -}) => { - const api = React.useRef(); - - const onReady = (event: SplitviewReadyEvent) => { - api.current = event.api; - - event.api.fromJSON({ - size: 100, - views: [ - { - size: 20, - data: { - id: 'panel1', - component: 'default', - params: { - color: 'red', - }, - }, - }, - { - size: 40, - data: { - id: 'panel2', - component: 'default', - params: { - color: 'green', - }, - }, - }, - { - size: 60, - data: { - id: 'panel3', - component: 'default', - params: { - color: 'purple', - }, - }, - }, - ], - orientation: props.orientation, - activeView: 'panel1', - }); - }; - - return ( - - ); -}; - -export default { - title: 'Library/Splitview/Deserialization', - component: Deserialization, - decorators: [ - (Component) => { - document.body.style.padding = '0px'; - return ( -
- -
- ); - }, - ], - args: { - orientation: Orientation.VERTICAL, - proportionalLayout: true, - disableAutoResizing: false, - }, - argTypes: { - orientation: { - control: { - type: 'inline-radio', - options: [Orientation.HORIZONTAL, Orientation.VERTICAL], - }, - }, - }, -} as Meta; diff --git a/packages/dockview-demo/src/stories/splitview/splitview.documentation.stories.mdx b/packages/dockview-demo/src/stories/splitview/splitview.documentation.stories.mdx deleted file mode 100644 index 45e2cf62e..000000000 --- a/packages/dockview-demo/src/stories/splitview/splitview.documentation.stories.mdx +++ /dev/null @@ -1,349 +0,0 @@ -import { Meta } from '@storybook/addon-docs/blocks'; - - - -import { Splitview } from './splitview'; -import VisualOne from './visual_1.jpg'; -import Contraints from './constraints.jpg'; - -# Splitview - -The splitview component is one of the libraries key components. -The paneview is an extension of thesplitview, the gridview is a collection of nested splitviews and the dockview adds tabular panels to the gridview. - -You can view several interactive examples of the Splitview component in action here, as well as read a description as to how this key component actually works. - -# The math behind the split view layouting - -The below is a language independant walkthrough of the math used within to layout these panels. This is the end result and below will explain the steps taken to reach this point. -The origins of this logic stem from the VSCode source code found [here](https://github.com/microsoft/vscode/tree/main/src/vs/base/browser/ui/splitview). - - - -## What is a Splitview? - -> A Splitview control is a collection of _views_ stacked either _horizontally_ or _vertically_ where each view can be indepedantly resized by dragging on the edge of a particular view. - -To explain how this works will require some better definitions so lets start with the two fundamental components of this control, the View and the Sash. - -Assume the Splitview control has n views where n is positive number (i.e if n=4 then our split view controls has 4 views). A single view can then be defined as - -
-
View
-
    -
  • - The size of the nth view will be known as Vn -
  • -
  • - The minimum size of the nth view will be known as V - min - n -
  • -
  • - The maximum size of the nth view will be known as V - max - n -
  • -
-
- -Additionally by definition we can known Vminn <= Vn <= Vmaxn - -To be able to resize a view you need to be able to drag on the edge of a view to increase or decrease it's size. -This can be achieved by introducing a narrow component that sits between each view acting as a _drag handle_. -Lets call this component a Sash (see [link](https://en.wikipedia.org/wiki/Sash_window)) and we can define this as - -
-
Sash
-
    -
  • If we have n views then we will have n-1 sashes. There is no sash before V0 nor after Vn
  • -
  • The sash between Vn and Vn+1 is known as Sn
  • -
  • The sash is of fixed width, and it's sole purpose is to act a drag-handle for resizing views
  • - -
-
- -To calculate the new view sizes after a sash is dragged we need to know which sash is being dragged. -Lets denote the sash Si as the sash to drag, which will give us a set of definitions to work with inline with the below diagram. - - - -If we are to drag the sash Si then it's also needed to know how far along the x-axis, or the y-axis (in the case of vertically stacked views) you have travelled. -Lets denote this as the delta, using the symbol Δ. Delta is only limited by the width (or height) of the control so in it's most general form we say it ranges from negative to positive infinity, that is -∞ < Δ < ∞ . -In reality as you will see we will apply a set of constraints on the value of Δ reducing it's overall set of valid values. - -This defines everything we need to describe the definition of a Splitview. The first approach to show will be the most native building the complexity after each iteration. - -## Iteration #1 - The naive approach (aka. the accordian) - -The most basic form of resizing may be to say as I add delta increase view sizes and as I remove delta I decrease view sizes. This could be further described with the following statements: - -- As the sash moves left shrink each view to the left and as the sash moves right expand each view to left, from right-most to left-most in both cases. -- If there is enough delta to shrink a view to it's mimimum size then progress onto the next view, and if we have enough delta to expand a view to it's maximum size then again progress onto the next view. -- Shrink no more once everything to the left is at minimums and expand no more once everything to the left is at maximums -- We don't manipulate any views to the right of the active sash Si - -You should be able to show each of the four points above hold true for the below interactive example. -You'll see that changes to the right will always remain at zero because we are not manipulating views to the right of the active sash. - - - -Putting this implemenation in psuedocode using the definitions from above where we drag sash Si by an amount Δ - -
-
- Δremaining = Δ -
-
- for - - (j = i; j >= 0; i--) - - do -
-
- - Vnext - j = Min(V - max - - j, Max(V - min - j, Vj + Δremaining)) - -
-
- - VΔ - j = Vnext - j - Vj - -
-
- - Δremaining = Δremaining - VΔ - j - -
-
- - Vj = Vnext - j - -
-
- -and as instructions - -
-
-
1
-
2
-
3
-
4
-
-
-
- For each view j to the left of the sash we have dragged, from - right-most to left-most -
-
- Add the delta to the view j (clamped at either the maximum or - minimum value) -
-
- Subtract the different between the new and old size (the used delta) - from the remaining delta -
-
repeat
-
-
- -There are some obvious flaws with this approach. Nothing to the right of the active sash is resizes which also related to the fact that the width of the control does not remain constant. - -## Iteration #2 - When Δ is added an equal Δ must be removed - -For the width of the control to remain constant it would make sense that if I add Δ to the left then I should add -Δ (or remove Δ) on the right, and vice-versa, which is the approach of the below interactive example. -As you may see it is right, but there are still some edge cases that fail. - - - -To write this approach in pseudocode lets define another variable to track the delta we've added on the left, Δused. -After we've applied changes to the left side we'll substract this Δused from the right side with the aim to keep the width of the control constant. - -
-
- Δremaining = Δ -
-
- Δused = 0 -
-
- for(j = i; j >= 0; i--) - do -
-
- - Vnextj = Min(Vmaxj, Max(Vminj, Vj + Δremaining)) - -
-
- - VΔj = Vnextj - Vj - -
-
- - Δremaining = Δremaining - VΔj - -
-
- - Δused = Δused + VΔj - -
-
- - Vj = Vnextj - -
- -
-
-for({"j = i+1; j < n; i++"}) - do -
-
- - Vnextj = Min(Vmaxj, Max(Vminj,Vj - Δused)) - -
-
- - VΔj = Vnextj - Vj - -
-
-used = Δused + VΔj - -
-
- -Vj = Vnextj - -
-
- -Go back and try to minimise or maximise every view in the container. The width is no longer preserved, you can see at some point the change to the left -is not longer eqaul to the change on the right, which causes the container to once again flex. - -## Iteration #3 - Constraining the values of Δ - -The failure of iteration #2 can be explained as the addition or removal of too much delta which means we need to look at what constraints we can apply to the problem. -For a sash Si think about the minimum and maximum amount of delta that can be both added and removed. - -Minimized view constraints - -- Si can go no further left that the sum of the minimum sizes of the views to the left because you would then have a view smaller than it's minimum size -- Si can go no further further right than the sum of the minimum sizes of the views to the right because you would then have a view smaller than it's minimum size - -Maximised view constraints - -- Si can go no further left that the sum of the maximum sizes of the views to the right because otherwise you would have a viewer larger than it's maximum size -- Si can go no further right that the sum of the maximum sizes of the views to the left because otherwise you would have a viewer larger than it's maximum size - -Since Δ is relative to Si we need these to define these constraints relative to Si. - -When the views to the left of Si are all at minimum size define the distance between here and Δ to be Δminleft. -This distance would be the sum of the differences between Vminj and Vj for each view: - -
- - Δmin - left = Σ Vmin - j - Vj - - j = i,...0 -
- -Similarly we can work out the distance between Si and the point at each every view to the left is at its -maximum size as the sum of differences between Vmaxj an Vj - -
- - Δmax - left = Σ Vmax - j - Vj - - j = i,...0 -
- -The same logic can be applied to work out those values for Δminright and Δmaxright - -
-
- - Δmin - right = Σ Vj - Vmin - j - - j = i+1...n -
-
- - Δmax - right = Σ Vj - Vmin - j - - j = i+1...n -
-
- -This leaves us with two minimum constraints which are Vminleft and Vmaxright and two maximum -constraints Vmaxleft and Vminright. -We can reduce these down to a single minimum and maximum contraint by taking the maximum of the two minimums and the minimum of the two maximums leaving us with the following constraints: - -
-
- Δmin = Max ( Vmin - left , Vmax - right ) -
-
- Δmax = Min ( Vmax - left , Vmin - right ) -
-
- -Given these constraints we can clamp the value of Δ to be within this minimum and maxium boundary. -This clamped delta can be used in place of delta in the pseudocode from Iteration #2. - -
- Δclamped = MIN ( Vmax , MAX ( Vmin , Δ ) ) -
- -You can see how this works in this interactive example which also visually indicates this boundary conditions. - - - -For a more visual explaination it's worth studying the below diagram: - - diff --git a/packages/dockview-demo/src/stories/splitview/splitview.params.stories.tsx b/packages/dockview-demo/src/stories/splitview/splitview.params.stories.tsx deleted file mode 100644 index 26edf2b2f..000000000 --- a/packages/dockview-demo/src/stories/splitview/splitview.params.stories.tsx +++ /dev/null @@ -1,107 +0,0 @@ -import { - ISplitviewPanelProps, - Orientation, - PanelCollection, - SplitviewApi, - SplitviewReact, - SplitviewReadyEvent, -} from 'dockview'; -import * as React from 'react'; -import { Meta } from '@storybook/react'; - -const components: PanelCollection> = { - default: (props) => { - return ( -
hello world
- ); - }, - ticker: (props: ISplitviewPanelProps<{ ticker: number }>) => { - return ( -
- {`The current ticker value is ${props.params.ticker}`} -
- ); - }, -}; - -export const Params = (props: { - theme: string; - hideBorders: boolean; - disableAutoResizing: boolean; -}) => { - const api = React.useRef(); - - React.useEffect(() => { - if (!api.current) { - return () => { - // noop - }; - } - - const gridApi = api.current; - - const interval = setInterval(() => { - const panel1 = gridApi.getPanel('panel1'); - - panel1.update({ params: { ticker: Date.now() } }); - }, 1000); - return () => { - clearInterval(interval); - }; - }, [api]); - - const onReady = (event: SplitviewReadyEvent) => { - api.current = event.api; - - event.api.addPanel({ - id: 'panel1', - component: 'ticker', - params: { - ticker: 0, - }, - }); - event.api.addPanel({ - id: 'panel2', - component: 'default', - }); - event.api.addPanel({ - id: 'panel3', - component: 'default', - }); - }; - - return ( - - ); -}; - -export default { - title: 'Library/Splitview/Params', - component: Params, - decorators: [ - (Component) => { - document.body.style.padding = '0px'; - return ( -
- -
- ); - }, - ], - args: { theme: 'dockview-theme-light' }, - argTypes: { - theme: { - control: { - type: 'select', - options: ['dockview-theme-dark', 'dockview-theme-light'], - }, - }, - }, -} as Meta; diff --git a/packages/dockview-demo/src/stories/splitview/splitview.persistance.stories.tsx b/packages/dockview-demo/src/stories/splitview/splitview.persistance.stories.tsx deleted file mode 100644 index d3b1cabbf..000000000 --- a/packages/dockview-demo/src/stories/splitview/splitview.persistance.stories.tsx +++ /dev/null @@ -1,138 +0,0 @@ -import { - ISplitviewPanelProps, - Orientation, - PanelCollection, - SerializedSplitview, - SplitviewApi, - SplitviewReact, - SplitviewReadyEvent, -} from 'dockview'; -import * as React from 'react'; -import { Story, Meta } from '@storybook/react'; - -const components: PanelCollection> = { - default: (props: ISplitviewPanelProps<{ color: string }>) => { - const resize = () => { - props.api.setSize({ size: 300 }); - }; - - return ( -
-
hello world
- -
- ); - }, -}; - -export const Persistance = (props: { - orientation: Orientation; - hideBorders: boolean; - proportionalLayout: boolean; - disableAutoResizing: boolean; -}) => { - const api = React.useRef(); - - const onReady = (event: SplitviewReadyEvent) => { - api.current = event.api; - - event.api.onDidLayoutChange(() => { - const state = event.api.toJSON(); - localStorage.setItem( - 'splitview.test.layout', - JSON.stringify(state) - ); - console.log(JSON.stringify(state, null, 4)); - }); - - const state = localStorage.getItem('splitview.test.layout'); - if (state) { - event.api.fromJSON(JSON.parse(state) as SerializedSplitview); - return; - } - - event.api.fromJSON({ - size: 100, - views: [ - { - size: 20, - data: { - id: 'panel1', - component: 'default', - params: { - color: 'red', - }, - }, - }, - { - size: 40, - data: { - id: 'panel2', - component: 'default', - params: { - color: 'green', - }, - }, - }, - { - size: 60, - data: { - id: 'panel3', - component: 'default', - params: { - color: 'purple', - }, - }, - }, - ], - orientation: props.orientation, - activeView: 'panel1', - }); - }; - - return ( - - ); -}; - -export default { - title: 'Library/Splitview/Persistance', - component: Persistance, - decorators: [ - (Component) => { - document.body.style.padding = '0px'; - return ( -
- -
- ); - }, - ], - args: { - orientation: Orientation.VERTICAL, - proportionalLayout: true, - disableAutoResizing: false, - }, - argTypes: { - orientation: { - control: { - type: 'inline-radio', - options: [Orientation.HORIZONTAL, Orientation.VERTICAL], - }, - }, - }, -} as Meta; diff --git a/packages/dockview-demo/src/stories/splitview/splitview.simple.stories.tsx b/packages/dockview-demo/src/stories/splitview/splitview.simple.stories.tsx deleted file mode 100644 index 7f1270978..000000000 --- a/packages/dockview-demo/src/stories/splitview/splitview.simple.stories.tsx +++ /dev/null @@ -1,110 +0,0 @@ -import { - ISplitviewPanelProps, - Orientation, - orthogonal, - PanelCollection, - SplitviewApi, - SplitviewReact, - SplitviewReadyEvent, -} from 'dockview'; -import * as React from 'react'; -import { Meta } from '@storybook/react'; - -const components: PanelCollection> = { - default: (props: ISplitviewPanelProps<{ color: string }>) => { - const resize = () => { - props.api.setSize({ size: 300 }); - }; - - const transpose = () => { - props.containerApi.updateOptions({ - orientation: orthogonal(props.containerApi.orientation), - }); - }; - - return ( -
-
hello world
- - -
- ); - }, -}; - -export const Simple = (props: { - orientation: Orientation; - hideBorders: boolean; - proportionalLayout: boolean; - disableAutoResizing: boolean; -}) => { - const api = React.useRef(); - - const onReady = (event: SplitviewReadyEvent) => { - api.current = event.api; - event.api.addPanel({ - id: 'panel1', - component: 'default', - params: { color: 'red' }, - minimumSize: 50, - }); - event.api.addPanel({ - id: 'panel2', - component: 'default', - params: { color: 'green' }, - minimumSize: 50, - }); - event.api.addPanel({ - id: 'panel3', - component: 'default', - params: { color: 'purple' }, - minimumSize: 50, - }); - }; - - return ( - - ); -}; - -export default { - title: 'Library/Splitview/Simple', - component: Simple, - decorators: [ - (Component) => { - document.body.style.padding = '0px'; - return ( -
- -
- ); - }, - ], - args: { - orientation: Orientation.VERTICAL, - proportionalLayout: true, - disableAutoResizing: false, - }, - argTypes: { - orientation: { - control: { - type: 'inline-radio', - options: [Orientation.HORIZONTAL, Orientation.VERTICAL], - }, - }, - }, -} as Meta; diff --git a/packages/dockview-demo/src/stories/splitview/splitview.snap.stories.tsx b/packages/dockview-demo/src/stories/splitview/splitview.snap.stories.tsx deleted file mode 100644 index 13b205bab..000000000 --- a/packages/dockview-demo/src/stories/splitview/splitview.snap.stories.tsx +++ /dev/null @@ -1,104 +0,0 @@ -import { - ISplitviewPanelProps, - Orientation, - PanelCollection, - SplitviewApi, - SplitviewReact, - SplitviewReadyEvent, -} from 'dockview'; -import * as React from 'react'; -import { Meta } from '@storybook/react'; - -const components: PanelCollection> = { - default: (props: ISplitviewPanelProps<{ color: string }>) => { - const resize = () => { - props.api.setSize({ size: 300 }); - }; - - return ( -
-
hello world
- -
- ); - }, -}; - -export const Snap = (props: { - orientation: Orientation; - hideBorders: boolean; - proportionalLayout: boolean; - disableAutoResizing: boolean; -}) => { - const api = React.useRef(); - - const onReady = (event: SplitviewReadyEvent) => { - api.current = event.api; - event.api.addPanel({ - id: 'panel1', - component: 'default', - params: { color: 'red' }, - minimumSize: 50, - snap: true, - }); - event.api.addPanel({ - id: 'panel2', - component: 'default', - params: { color: 'green' }, - minimumSize: 50, - }); - event.api.addPanel({ - id: 'panel3', - component: 'default', - params: { color: 'purple' }, - minimumSize: 50, - snap: true, - }); - }; - - return ( - - ); -}; - -export default { - title: 'Library/Splitview/Snap', - component: Snap, - decorators: [ - (Component) => { - document.body.style.padding = '0px'; - return ( -
- -
- ); - }, - ], - args: { - orientation: Orientation.VERTICAL, - proportionalLayout: true, - disableAutoResizing: false, - }, - argTypes: { - orientation: { - control: { - type: 'inline-radio', - options: [Orientation.HORIZONTAL, Orientation.VERTICAL], - }, - }, - }, -} as Meta; diff --git a/packages/dockview-demo/src/stories/splitview/splitview.tsx b/packages/dockview-demo/src/stories/splitview/splitview.tsx deleted file mode 100644 index 6854aff85..000000000 --- a/packages/dockview-demo/src/stories/splitview/splitview.tsx +++ /dev/null @@ -1,341 +0,0 @@ -import * as React from 'react'; -import './splitview.css'; - -const min = 100; -const max = 300; - -interface IDebugResize { - leftmin: number; - leftmax: number; - rightmin: number; - rightmax: number; - min: number; - max: number; -} - -const resize = ( - index: number, - delta: number, - sizes: number[], - mode: number -) => { - const nextSizes = [...sizes]; - - const left = nextSizes.filter((_, i) => i <= index); - const right = nextSizes.filter((_, i) => i > index); - - let result: IDebugResize = { - leftmin: undefined, - leftmax: undefined, - rightmin: undefined, - rightmax: undefined, - max: undefined, - min: undefined, - }; - - // step 3 - if (mode > 2) { - const leftMinimumsDelta = left - .map((x) => min - x) - .reduce((x, y) => x + y, 0); - const leftMaximumsDelta = left - .map((x) => max - x) - .reduce((x, y) => x + y, 0); - const rightMinimumsDelta = right - .map((x) => x - min) - .reduce((x, y) => x + y, 0); - const rightMaximumsDelta = right - .map((x) => x - max) - .reduce((x, y) => x + y, 0); - const _min = Math.max(leftMinimumsDelta, rightMaximumsDelta); - const _max = Math.min(leftMaximumsDelta, rightMinimumsDelta); - const clamp = Math.max(_min, Math.min(_max, delta)); - - result = { - leftmin: leftMinimumsDelta, - leftmax: leftMaximumsDelta, - rightmin: rightMinimumsDelta, - rightmax: rightMaximumsDelta, - max: _max, - min: _min, - }; - delta = clamp; - } - - let usedDelta = 0; - let remainingDelta = delta; - - // Step 1 - for (let i = left.length - 1; i > -1; i--) { - const x = Math.max(min, Math.min(max, left[i] + remainingDelta)); - const viewDelta = x - left[i]; - usedDelta += viewDelta; - remainingDelta -= viewDelta; - left[i] = x; - } - - // Step 2 - if (mode > 1) { - for (let i = 0; i < right.length; i++) { - const x = Math.max(min, Math.min(max, right[i] - usedDelta)); - const viewDelta = x - right[i]; - usedDelta += viewDelta; - right[i] = x; - } - } - - return { ...result, sizes: [...left, ...right] }; -}; - -interface ILayoutState { - sashes: number[]; - views: number[]; - deltas: number[]; - left: number; - right: number; - debug: IDebugResize; - drag: number; -} - -export const Splitview = (props: { mode: number; debug: boolean }) => { - // keep the sashes and views in one state to prevent weird out-of-sync-ness - const [layout, setLayout] = React.useState({ - sashes: [200, 400, 600], - views: [200, 200, 200, 200], - deltas: [0, 0, 0, 0], - left: 0, - right: 0, - debug: undefined, - drag: -1, - }); - - const ref = React.useRef(); - - const onMouseDown = (index: number) => (ev: React.MouseEvent) => { - const start = ev.clientX; - const sizes = [...layout.views]; - - const mousemove = (ev: MouseEvent) => { - const current = ev.clientX; - const delta = current - start; - const { - sizes: nextLayout, - rightmin, - rightmax, - leftmin, - leftmax, - max, - min, - } = resize(index, delta, sizes, props.mode); - const sashes = nextLayout.reduce( - (x, y) => [...x, y + (x.length === 0 ? 0 : x[x.length - 1])], - [] - ); - sashes.splice(sashes.length - 1, 1); - const deltas = sizes.map((x, i) => nextLayout[i] - x); - - const offset = start - ref.current?.getBoundingClientRect().left; - - setLayout({ - views: nextLayout, - sashes, - deltas, - left: deltas - .filter((_, i) => i <= index) - .reduce((x, y) => x + y, 0), - right: deltas - .filter((_, i) => i > index) - .reduce((x, y) => x + y, 0), - debug: { - leftmax: leftmax + offset, - leftmin: leftmin + offset, - rightmax: rightmax + offset, - rightmin: rightmin + offset, - min: min + offset, - max: max + offset, - }, - drag: index, - }); - }; - - const end = (ev: MouseEvent) => { - document.removeEventListener('mousemove', mousemove); - document.removeEventListener('mouseup', end); - setLayout((_) => ({ - ..._, - deltas: _.deltas.map((_) => 0), - left: 0, - right: 0, - drag: -1, - })); - }; - - document.addEventListener('mousemove', mousemove); - document.addEventListener('mouseup', end); - }; - - const extras = React.useMemo(() => { - if (!props.debug || !layout.debug || props.mode < 3) { - return null; - } - return ( - <> -
- left-max -
-
- left-min -
-
- right-max -
-
- right-min -
-
-
-
-
- - ); - }, [layout.debug]); - - return ( -
- {props.debug && ( -
- {`Change to left ${layout?.left}`} - {`Change to right ${layout?.right}`} - {`Total size ${layout?.views.reduce( - (x, y) => x + y, - 0 - )}`} -
- )} -
-
- {layout.sashes.map((x, i) => { - const className = - layout.drag === i ? 'sash drag-sash' : 'sash'; - return ( -
- ); - })} - {extras} -
-
- {layout.views.map((x, i) => { - const isMax = x >= max; - const isMin = x <= min; - - return ( -
- {props.debug && ( - <> -
- {`${layout.views[i]} (${ - layout.deltas[i] > -1 ? '+' : '' - }${layout.deltas[i]})`} -
-
{`isMin = ${isMin}`}
-
{`isMax = ${isMax}`}
- - )} -
- ); - })} -
-
-
- ); -}; diff --git a/packages/dockview-demo/src/stories/splitview/visual_1.jpg b/packages/dockview-demo/src/stories/splitview/visual_1.jpg deleted file mode 100644 index 6f7375dc5..000000000 Binary files a/packages/dockview-demo/src/stories/splitview/visual_1.jpg and /dev/null differ diff --git a/packages/dockview-demo/tsconfig.json b/packages/dockview-demo/tsconfig.json deleted file mode 100644 index f09a12241..000000000 --- a/packages/dockview-demo/tsconfig.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "extends": "../../module-build/tsconfig.json", - "compilerOptions": { - "outDir": "dist", - "tsBuildInfoFile": ".build/tsconfig.tsbuildinfo.cjs", - "jsx": "react", - "rootDir": "src", - "allowUnreachableCode": true, - "noImplicitAny": false, - "sourceMap": true, - "declaration": false, - "strictNullChecks":false - }, - "include": ["src"], - "exclude": ["**/node_modules", "src/__tests__"] -} \ No newline at end of file diff --git a/packages/dockview-demo/webpack.config.js b/packages/dockview-demo/webpack.config.js deleted file mode 100644 index 318ce6d28..000000000 --- a/packages/dockview-demo/webpack.config.js +++ /dev/null @@ -1,52 +0,0 @@ -const path = require('path'); - -module.exports = { - entry: path.resolve(__dirname, 'src/index.tsx'), - devtool: 'source-map', - output: { - filename: 'bundle.js', - path: path.resolve(__dirname, 'dist'), - publicPath: '/dist', - }, - mode: 'development', - resolve: { - extensions: ['.ts', '.js', '.tsx', 'jsx'], - // alias: { - // react: path.resolve(__dirname, 'node_modules/react'), - // 'react-dom': path.resolve(__dirname, 'node_modules/react-dom'), - // }, - }, - module: { - rules: [ - { - test: /\.tsx?$/, - loader: 'ts-loader', - }, - { - test: /\.s[ac]ss$/i, - use: [ - // Creates `style` nodes from JS strings - 'style-loader', - // Translates CSS into CommonJS - 'css-loader', - // Compiles Sass to CSS - 'sass-loader', - ], - }, - { - test: /\.css$/i, - use: [ - // Creates `style` nodes from JS strings - 'style-loader', - // Translates CSS into CommonJS - 'css-loader', - ], - }, - ], - }, - devServer: { - port: 9000, - compress: true, - static: path.resolve(__dirname, 'public'), - }, -};